// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--==
//=============================================================================
//
// Class: LocalDataStore
//
// Purpose: Class that stores local data. This class is used in cooperation
//          with the _LocalDataStoreMgr class.
//
//=============================================================================  

namespace System
{

    using System;

    internal class LocalDataStore
    {
        //=========================================================================
        // DON'T CHANGE THESE UNLESS YOU MODIFY LocalDataStoreBaseObject in vm\object.h
        //=========================================================================  
        private Object[] m_DataTable;
        private LocalDataStoreMgr m_Manager;

        //=========================================================================
        // Initialize the data store.
        //=========================================================================  
        public LocalDataStore(LocalDataStoreMgr mgr, int InitialCapacity)
        {
            if (null == mgr)
                throw new ArgumentNullException("mgr");

            // Store the manager of the local data store.
            m_Manager = mgr;

            // Allocate the array that will contain the data.
            m_DataTable = new Object[InitialCapacity];
        }

        //=========================================================================
        // Retrieves the value from the specified slot.
        //=========================================================================  
        public Object GetData(LocalDataStoreSlot slot)
        {
            Object o = null;

            // Validate the slot.
            m_Manager.ValidateSlot(slot);

            // Cache the slot index to avoid synchronization issues.
            int slotIdx = slot.Slot;

            if (slotIdx >= 0) {
                // Delay expansion of m_DataTable if we can
                if (slotIdx >= m_DataTable.Length)
                    return null;

                // Retrieve the data from the given slot.
                o = m_DataTable[slotIdx];
            }

            // Check if the slot has become invalid.
            if (!slot.IsValid())
                throw new InvalidOperationException("InvalidOperation_SlotHasBeenFreed");
            return o;
        }

        //=========================================================================
        // Sets the data in the specified slot.
        //=========================================================================  
        public void SetData(LocalDataStoreSlot slot, Object data)
        {
            // Validate the slot.
            m_Manager.ValidateSlot(slot);

            // I can't think of a way to avoid the race described in the
            // LocalDataStoreSlot finalizer method without a lock.
            lock (m_Manager) {
                if (!slot.IsValid())
                    throw new InvalidOperationException("InvalidOperation_SlotHasBeenFreed");

                // Do the actual set operation.
                SetDataInternal(slot.Slot, data, true /*bAlloc*/ );
            }
        }

        //=========================================================================
        // This method does the actual work of setting the data.
        //=========================================================================  
        internal void SetDataInternal(int slot, Object data, bool bAlloc)
        {
            // We try to delay allocate the dataTable (in cases like the manager clearing a
            // just-freed slot in all stores
            if (slot >= m_DataTable.Length) {
                if (!bAlloc)
                    return;
                SetCapacity(m_Manager.GetSlotTableLength());
            }

            // Set the data on the given slot.
            m_DataTable[slot] = data;
        }

        //=========================================================================
        // Method used to set the capacity of the local data store.
        //=========================================================================  
        private void SetCapacity(int capacity)
        {
            // Validate that the specified capacity is larger than the current one.
            if (capacity < m_DataTable.Length)
            throw new ArgumentException("Argument_ALSInvalidCapacity");

            // Allocate the new data table.
            Object[] NewDataTable = new Object[capacity];

            // Copy all the objects into the new table.
            Array.Copy(m_DataTable, NewDataTable, m_DataTable.Length);

            // Save the new table.
            m_DataTable = NewDataTable;
        }
    }
}
