File: Core\CSharp\System\Windows\Media\VisualProxy.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
//------------------------------------------------------------------------------
//
// <copyright file="VisualProxy.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//      Mapping between a visual and its handles on different channels.
//
//------------------------------------------------------------------------------
 
namespace System.Windows.Media.Composition
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Media;
 
 
    /// <summary>
    /// Mapping between a visual and its handles on different channels.
    /// </summary>
    internal struct VisualProxy
    {
        // --------------------------------------------------------------------
        //
        //   Private Types
        //
        // --------------------------------------------------------------------
 
        #region Private Types
 
        /// <summary>
        /// Tuple binding a channel to a set of flags and a resource handle.
        /// </summary>
        /// <remarks>
        /// Note that DUCE.Channel is a reference type.
        /// </remarks>
        private struct Proxy
        {
            internal DUCE.Channel Channel;
            internal VisualProxyFlags Flags;
            internal DUCE.ResourceHandle Handle;
        }
 
        #endregion Private Types
 
 
        // --------------------------------------------------------------------
        //
        //   Internal Properties
        //
        // --------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Returns the number of channels this resource is marshaled to.
        /// </summary>
        internal int Count
        {
            get
            {
                if (_tail == null) 
                {
                    return _head.Channel == null ? 0 : 1;
                }
                else
                {
                    //
                    // The logic here is simple: we keep at most one entry
                    // in the tail free. Heads has to be occupied and adds one.
                    //
 
                    int tailLength = _tail.Length;
 
                    bool lastTailIsEmpty = 
                        _tail[tailLength - 1].Channel == null;
 
                    return 1 + tailLength - (lastTailIsEmpty ? 1 : 0);
                }
            }
        }
 
 
        /// <summary>
        /// Returns true if the parent resource is marshaled to at least
        /// one channel.
        /// </summary>
        internal bool IsOnAnyChannel
        {
            get
            {
                return Count != 0;
            }
        }
 
        #endregion Internal Properties
 
 
 
        // --------------------------------------------------------------------
        //
        //   Internal Methods
        //
        // --------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Returns true if the visual is marshaled to the
        /// specified channel.
        /// </summary>
        internal bool IsOnChannel(DUCE.Channel channel)
        {
            int index = Find(channel);
 
            if (index == PROXY_NOT_FOUND) 
            {
                return false;
            }
            else if (index == PROXY_STORED_INLINE) 
            {
                return !_head.Handle.IsNull;
            }
            else 
            {
                return !_tail[index].Handle.IsNull;
            }
        }
 
        /// <summary>
        /// If the visual is on channel, its reference count is increased.
        /// Otherwise, a new resource is created on that channel.
        /// </summary>
        internal bool CreateOrAddRefOnChannel(
            object instance,
            DUCE.Channel channel,
            DUCE.ResourceType resourceType)
        {
            int index = Find(channel);
            int count = Count;
 
            if (index == PROXY_NOT_FOUND) 
            {
                //
                // This is the case where we have to create a new resource.
                //
 
                if (_head.Channel == null) 
                {
                    //
                    // We're adding the first proxy.
                    // 
                    // Before:        [empty]
                    // After insert:  [ head]
                    //
 
                    Debug.Assert(count == 0);
 
                    _head.Channel = channel;
                    _head.Flags = VisualProxyFlags.None;
 
                    channel.CreateOrAddRefOnChannel(
                        instance,
                        ref _head.Handle, 
                        resourceType);
                }
                else
                {
                    if (_tail == null) 
                    {
                        //
                        // We're adding the second proxy.
                        // 
                        // Before:        [head]
                        // After resize:  [head] [ empty] [empty]
                        // After insert:  [head] [tail 0] [empty]
                        //                       ----------------
 
                        Debug.Assert(count == 1);
 
                        _tail = new Proxy[2];
                    }
                    else if (count > _tail.Length) 
                    {
                        //
                        // Increase the tail size by 2.
                        //                
                        // Before:        [head] [tail 0] ... [tail c-2]
                        // After resize:  [head] [tail 0] ... [tail c-2] [   empty] [empty]
                        // After insert:  [head] [tail 0] ... [tail c-3] [tail c-2] [empty]
                        //                       ------------------------------------------
                        //
 
                        ResizeTail(2);
                    }
 
                    //
                    // Now that we have a tail, fill in the first free element.
                    //
 
                    Proxy proxy;
 
                    proxy.Channel = channel;
                    proxy.Flags = VisualProxyFlags.None;
                    proxy.Handle = DUCE.ResourceHandle.Null;
 
                    channel.CreateOrAddRefOnChannel(
                        instance,
                        ref proxy.Handle, 
                        resourceType);
 
                    _tail[count - 1] = proxy;
                }
 
                return /* created */ true;
            }
            else if (index == PROXY_STORED_INLINE) 
            {
                //
                // We simply need to increase the reference count on head...
                //
 
                channel.CreateOrAddRefOnChannel(
                    instance,
                    ref _head.Handle, 
                    resourceType);
            }
            else
            {
                //
                // Increase the reference count on one of the tail proxies...
                //
 
                channel.CreateOrAddRefOnChannel(
                    instance,
                    ref _tail[index].Handle, 
                    resourceType);
            }
 
            return /* not created */ false;
        }
 
 
        /// <summary>
        /// If visual is on channel, its reference count is increased.
        /// Otherwise, a new resource is created on that channel.
        /// </summary>
        internal bool ReleaseOnChannel(DUCE.Channel channel)
        {
            int index = Find(channel);
            bool proxyRemoved = false;
            int count = Count;
 
            if (index == PROXY_STORED_INLINE) 
            {
                if (channel.ReleaseOnChannel(_head.Handle)) 
                {
                    //
                    // Need to move the last of the non-empty tail to head
                    // or clear the head. Erase the head if that was the last
                    // proxy.
                    //
 
                    if (count == 1) 
                    {
                        _head = new Proxy();
                    }
                    else
                    {
                        _head = _tail[count - 2];
                    }
 
                    proxyRemoved = true;
                }
            }
            else if (index >= 0) 
            {
                if (channel.ReleaseOnChannel(_tail[index].Handle)) 
                {
                    //
                    // Need to move the last of the non-empty tail to the
                    // removed index. Avoid in-place copying.
                    //
 
                    if (index != count - 2) 
                    {
                        _tail[index] = _tail[count - 2];
                    }
 
                    proxyRemoved = true;
                }
            }
            else
            {
                Debug.Assert(index != PROXY_NOT_FOUND);
                return false;
            }
 
            if (proxyRemoved) 
            {
                if (_tail != null) 
                {
                    //
                    // Keep the tail short. We allow for one extra free element
                    // in tail to avoid constant allocations / reallocations.
                    //
 
                    if (count == 2) 
                    {
                        //                        ------------------
                        // Before removal: [head] [tail c-1] [empty]
                        // Here and now:   [head] [ deleted] [empty]
                        // After removal:  [head]
                        //
 
                        _tail = null;
                    }
                    else if (count == _tail.Length) 
                    {
                        //                        ---------------------------------------------------
                        // Before removal: [head] [tail 0] [tail 1] ... [tail c-3] [tail c-2] [empty]
                        // Here and now:   [head] [tail 0] [tail 1] ... [tail c-3] [ deleted] [empty]
                        // After removal:  [head] [tail 0] [tail 1] ... [tail c-3]
                        //
 
                        ResizeTail(-2);
                    }
                    else
                    {
                        //                        ------------------------------------------------------
                        // Before removal: [head] [tail 0] [tail 1] ... [tail c-4] [tail c-3] [tail c-2]
                        // Here and now:   [head] [tail 0] [tail 1] ... [tail c-4] [tail c-3] [ deleted]
                        // After removal:  [head] [tail 0] [tail 1] ... [tail c-4] [tail c-3] [   empty]
                        //
 
                        _tail[count - 2] = new Proxy();
                    }
                }
            }
 
            return proxyRemoved;
        }
 
 
        /// <summary>
        /// Returns the channel that the n-th proxy connects to.
        /// </summary>
        internal DUCE.Channel GetChannel(int index)
        {
            Debug.Assert(index >= 0 && index < Count);
 
            if (index < Count) 
            {
                if (index == 0) 
                {
                    return _head.Channel;
                }
                else if (index > 0) 
                {
                    return _tail[index - 1].Channel;
                }
            }
 
            return null;                
        }
 
        #endregion Internal Methods
 
 
 
        // ----------------------------------------------------------------
        //
        //   Internal Methods: resource handle getters
        //
        // ----------------------------------------------------------------
 
        #region Internal Methods: resource handle getters
 
        /// <summary>
        /// Returns the handle the visual has on a specific channel.
        /// </summary>
        internal DUCE.ResourceHandle GetHandle(DUCE.Channel channel)
        {
            return GetHandle(Find(channel) + 1); // Find's results are -1 based, adjust by one.
        }
 
 
        /// <summary>
        /// Returns the handle the visual has on the n-th channel.
        /// </summary>
        internal DUCE.ResourceHandle GetHandle(int index)
        {
            Debug.Assert(index >= 0 && index < Count);
 
            if (index < Count) 
            {
                if (index == 0) 
                {
                    return _head.Handle;
                }
                else if (index > 0) 
                {
                    return _tail[index - 1].Handle;
                }
            }
 
            return DUCE.ResourceHandle.Null;                
        }
 
        #endregion Internal Methods: resource handle getters
 
 
 
        // ----------------------------------------------------------------
        //
        //   Internal Methods: visual proxy flags
        //
        // ----------------------------------------------------------------
 
        #region Internal Methods: visual proxy flags
 
        /// <summary>
        /// Returns the flags the visual has on a specific channel.
        /// </summary>
        internal VisualProxyFlags GetFlags(DUCE.Channel channel)
        {
            return GetFlags(Find(channel) + 1); // Find's results are -1 based, adjust by one.
        }
 
 
        /// <summary>
        /// Returns the handle the visual has on n-th channel.
        /// </summary>
        internal VisualProxyFlags GetFlags(int index)
        {
            if (index < Count) 
            {
                if (index == 0) 
                {
                    return _head.Flags;
                }
                else if (index > 0) 
                {
                    return _tail[index - 1].Flags;
                }
            }
 
            return VisualProxyFlags.None;                
        }
 
 
        /// <summary>
        /// Sets the flags the visual has on a specific channel.
        /// </summary>
        internal void SetFlags(
            DUCE.Channel channel,
            bool value,
            VisualProxyFlags flags)
        {
            SetFlags(Find(channel) + 1, value, flags); // Find's results are -1 based, adjust by one.
        }
 
 
        /// <summary>
        /// Sets the flags the visual has on the n-th channel.
        /// </summary>
        internal void SetFlags(
            int index,
            bool value,
            VisualProxyFlags flags)
        {
            Debug.Assert(index >= 0 && index < Count);
 
            if (index < Count) 
            {
                if (index == 0) 
                {
                    _head.Flags = 
                        value ? (_head.Flags | flags) : (_head.Flags & ~flags);
                }
                else if (index > 0) 
                {
                    _tail[index - 1].Flags = 
                        value ? (_tail[index - 1].Flags | flags) : (_tail[index - 1].Flags & ~flags);
                }
            }
        }
 
 
        /// <summary>
        /// Sets the flags on all channels the visual is marshaled to.
        /// </summary>
        internal void SetFlagsOnAllChannels(
            bool value,
            VisualProxyFlags flags)
        {
            if (_head.Channel != null) 
            {
                _head.Flags = 
                    value ? (_head.Flags | flags) : (_head.Flags & ~flags);
 
                for (int i = 0, limit = Count - 1; i < limit; i++) 
                {
                    _tail[i].Flags = 
                        value ? (_tail[i].Flags | flags) : (_tail[i].Flags & ~flags);
                }
            }
        }
 
        /// <summary>
        /// Returns true if the given flags are set for every proxy or if
        /// the visual is not being marshaled.
        /// </summary>        
        internal bool CheckFlagsOnAllChannels(
            VisualProxyFlags conjunctionFlags)
        {
            if (_head.Channel != null) 
            {
                if ((_head.Flags & conjunctionFlags) != conjunctionFlags)
                    return false;
 
                for (int i = 0, limit = Count - 1; i < limit; i++) 
                {
                    if ((_tail[i].Flags & conjunctionFlags) != conjunctionFlags)
                        return false;
                }
            }
 
            return true;
        }
 
        #endregion Internal Methods: visual proxy flags
 
 
        // --------------------------------------------------------------------
        //
        //   Private Methods
        //
        // --------------------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        /// Looks for the given channel in the proxies list.
        /// </summary>
        private int Find(DUCE.Channel channel)
        {
            if (_head.Channel == channel) 
            {
                return PROXY_STORED_INLINE;
            }
            else if (_tail != null) 
            {
                for (int i = 0, limit = Count - 1; i < limit; i++) 
                {
                    if (_tail[i].Channel == channel) 
                    {
                        return i;
                    }
                }
            }
 
            return PROXY_NOT_FOUND;
        }
 
        /// <summary>
        /// Grows or shrinks the tail by a given size.
        /// </summary>
        private void ResizeTail(int delta)
        {
            int newLength = _tail.Length + delta;
 
            Debug.Assert(delta % 2 == 0 && newLength >= 2);
 
            Proxy[] reallocatedTail = new Proxy[newLength];
 
            Array.Copy(_tail, reallocatedTail, Math.Min(_tail.Length, newLength));
 
            _tail = reallocatedTail;
        }
        
        #endregion Private Methods
 
 
 
        // --------------------------------------------------------------------
        //
        //   Private Fields
        //
        // --------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// Returned by the Find method to denote that a proxy for the given
        /// channel has not been found.
        /// </summary>
        private const int PROXY_NOT_FOUND = -2;
 
        /// <summary>
        /// Returned by the Find method to denote that a proxy for the given
        /// channel has been found in the inline storage.
        /// </summary>
        private const int PROXY_STORED_INLINE = -1;
 
        /// <summary>
        /// This data structure is optimized for single entry. _head is 
        /// the one entry that we inline into the struct for that purpose.
        /// </summary>
        private Proxy _head;
 
        /// <summary>
        /// All the other entries end up in this array.
        /// </summary>
        private Proxy[] _tail;
 
        #endregion Private Fields
    }
}