//-----------------------------------------------------------------------
// 
//     Copyright (c) Microsoft Corporation.
// 
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop.Implementation
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    /// 
    /// A collection of GCHandles for pinned objects. The handles
    /// are freed when this object is disposed.
    /// 
    [StructLayout(LayoutKind.Auto)]
    internal struct GCHandleCollection : IDisposable
    {
        /// 
        /// The handles of the objects being pinned.
        /// 
        private GCHandle[] handles;
        /// 
        /// Handle count.
        /// 
        private int count;
        /// 
        /// Disposes of the object.
        /// 
        public void Dispose()
        {
            if (null != this.handles)
            {
                for (int i = 0; i < this.count; i++)
                {
                    this.handles[i].Free();
                }
                this.handles = null;
            }
        }
        /// 
        /// Add an object to the handle collection. This automatically
        /// pins the object.
        /// 
        /// The object to pin.
        /// 
        /// The address of the pinned object. This is valid until the
        /// GCHandleCollection is disposed.
        /// 
        public IntPtr Add(object value)
        {
            if (null == value)
            {
                return IntPtr.Zero;
            }
            if (null == this.handles)
            {
                this.handles = new GCHandle[4]; // same as List
            }
            else if (this.count == this.handles.Length)
            {
                Array.Resize(ref this.handles, this.count * 2);
            }
            Debug.Assert(this.count < this.handles.Length, "Index out of bound");
            GCHandle handle = GCHandle.Alloc(value, GCHandleType.Pinned);
            this.handles[this.count++] = handle;
            IntPtr pinned = handle.AddrOfPinnedObject();
            Debug.Assert(IntPtr.Zero != pinned, "Pinned object has null address");
            return pinned;
        }
        /// 
        /// Set handle array capacity.
        /// 
        /// Estimated handle count
        public void SetCapacity(int capacity)
        {
            if (null == this.handles)
            {
                this.handles = new GCHandle[capacity];
            }
        }
    }
}