//
// Copyright (c) Microsoft Corporation.   All rights reserved.
//

/*******************************************************************/
/*                           WARNING                               */
/* This file should be identical in the Bartok and Singularity     */
/* depots. Master copy resides in Bartok Depot. Changes should be  */
/* made to Bartok Depot and propagated to Singularity Depot.       */
/*******************************************************************/


namespace System.GCs {

    using Microsoft.Bartok.Runtime;
    using System.Threading;
    using System.Runtime.CompilerServices;

    internal unsafe class BootstrapMemory : Allocator
    {

        // WARNING: don't initialize any static fields in this class
        // without manually running the class constructor at startup!

        private static UIntPtr allocPtr;
        private static UIntPtr limitPtr;

        [PreInitRefCounts]
#if !SINGULARITY                // Needed before Bartok runtime is initialized
        [NoStackLinkCheck]
#endif
        [NoBarriers]
        internal static void Initialize(UIntPtr systemMemorySize) {
            allocPtr = MemoryManager.AllocateMemory(systemMemorySize);
            limitPtr = allocPtr + systemMemorySize;
            if(GC.gcType != GCType.NullCollector) {
                PageManager.SetStaticDataPages(allocPtr, systemMemorySize);
#if !SINGULARITY
                PageTable.SetProcess(PageTable.Page(allocPtr),
                                     PageTable.PageCount(systemMemorySize));
#endif
            }
        }

        [NoBarriers]
        private static UIntPtr AllocateBlock(UIntPtr bytes, uint alignment)
        {
            UIntPtr startPtr =
                Allocator.AlignedAllocationPtr(allocPtr, limitPtr, alignment);
            allocPtr = startPtr + bytes;
            if (allocPtr > limitPtr) {
                VTable.DebugPrint("Out of BootstrapMemory");
                VTable.DebugBreak();
            }
            return startPtr + PreHeader.Size;
        }

        [NoBarriers]
        [ManualRefCounts]
#if !SINGULARITY                // Needed before Bartok runtime is initialized
        [NoStackLinkCheckTrans]
#endif
        internal static Object Allocate(VTable vtable) {
            UIntPtr numBytes = ObjectLayout.ObjectSize(vtable);
            UIntPtr objectAddr = AllocateBlock(numBytes, vtable.baseAlignment);
            Object result = Magic.fromAddress(objectAddr);
#if REFERENCE_COUNTING_GC
            uint refState = vtable.isAcyclicRefType ?
                (ReferenceCountingCollector.
                acyclicFlagMask | 2) : 2;
            result.REF_STATE = refState &
                ~ReferenceCountingCollector.countingONFlagMask;
#elif DEFERRED_REFERENCE_COUNTING_GC
            uint refState = vtable.isAcyclicRefType ?
                (DeferredReferenceCountingCollector.
                 acyclicFlagMask |
                 DeferredReferenceCountingCollector.
                 markFlagMask) :
                DeferredReferenceCountingCollector.
                markFlagMask;
            result.REF_STATE = refState &
                ~DeferredReferenceCountingCollector.countingONFlagMask;
#endif
            Barrier.BootstrapInitObject(result, vtable);
            return result;
        }

        [NoBarriers]
        [ManualRefCounts]
#if !SINGULARITY                // Needed before Bartok runtime is initialized
        [NoStackLinkCheckTrans]
#endif
        internal static Object Allocate(VTable vtable, uint count) {
            UIntPtr numBytes = ObjectLayout.ArraySize(vtable, count);
            UIntPtr objectAddr = AllocateBlock(numBytes, vtable.baseAlignment);
            Array result = Magic.toArray(Magic.fromAddress(objectAddr));
#if REFERENCE_COUNTING_GC
            uint refState = vtable.isAcyclicRefType ?
                (ReferenceCountingCollector.
                acyclicFlagMask | 2) : 2;
            result.REF_STATE = refState &
                ~ReferenceCountingCollector.countingONFlagMask;
#elif DEFERRED_REFERENCE_COUNTING_GC
            uint refState = vtable.isAcyclicRefType ?
                (DeferredReferenceCountingCollector.
                 acyclicFlagMask |
                 DeferredReferenceCountingCollector.
                 markFlagMask) :
                DeferredReferenceCountingCollector.
                markFlagMask;
            result.REF_STATE = refState &
                ~DeferredReferenceCountingCollector.countingONFlagMask;
#endif
            Barrier.BootstrapInitObject(result, vtable);
            result.InitializeVectorLength((int) count);
            return result;
        }

        [NoBarriers]
        [PreInitRefCounts]
#if !SINGULARITY
        [NoStackLinkCheckTrans]
#endif
        internal static Object Allocate(Type t) {
            return Allocate(Magic.toRuntimeType(t));
        }

        [NoBarriers]
        [PreInitRefCounts]
#if !SINGULARITY
        [NoStackLinkCheckTrans]
#endif
        internal static Object Allocate(Type t, uint count) {
            return Allocate(Magic.toRuntimeType(t), count);
        }

        [NoBarriers]
        [PreInitRefCounts]
#if !SINGULARITY
        [NoStackLinkCheckTrans]
#endif
        internal static Object Allocate(RuntimeType t) {
            return Allocate(t.classVtable);
        }

        [NoBarriers]
        [PreInitRefCounts]
#if !SINGULARITY
        [NoStackLinkCheckTrans]
#endif
        internal static Object Allocate(RuntimeType t, uint count) {
            return Allocate(t.classVtable, count);
        }

        internal static void Truncate() {
            UIntPtr allocLimit = PageTable.PagePad(allocPtr);
            UIntPtr unusedSize = limitPtr - allocLimit;
            if(GC.gcType != GCType.NullCollector) {
                PageManager.ReleaseUnusedPages(PageTable.Page(allocLimit),
                                               PageTable.PageCount(unusedSize),
                                               true);
            }
            limitPtr = allocLimit;
        }

    }

}
