//
// 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.Runtime.CompilerServices;

    [NoCCtor]
    internal abstract class Allocator
    {

        [Inline]
        [NoHeapAllocation]
        internal static unsafe void WriteAlignment(UIntPtr addr)
        {
            *(UIntPtr*)addr = ALIGNMENT_TOKEN;
        }

        [Inline]
        [NoHeapAllocation]
        internal static unsafe bool IsAlignmentMarkerAddr(UIntPtr addr)
        {
            return (*(UIntPtr*)addr == ALIGNMENT_TOKEN);
        }

        [Inline]
        [NoHeapAllocation]
        internal static unsafe bool IsAlignment(UIntPtr obj)
        {
            return *(UIntPtr*)(obj - PreHeader.Size) == ALIGNMENT_TOKEN;
        }

        [Inline]
        [NoHeapAllocation]
        internal static UIntPtr SkipAlignment(UIntPtr obj)
        {
            while (IsAlignment(obj)) {
                obj += UIntPtr.Size;
            }
            return obj;
        }
      
        // Pads the allocation to the appropriate alignment.
        [Inline]
        internal static UIntPtr AlignedAllocationPtr(UIntPtr startAddr,
                                                     UIntPtr limitAddr,
                                                     uint alignment)
        {
            if (alignment > UIntPtr.Size) {
                uint alignmentMask = alignment - 1;
                int offset = PreHeader.Size + PostHeader.Size;
                while (((startAddr + offset) & alignmentMask) != 0 &&
                       startAddr < limitAddr) {
                    Allocator.WriteAlignment(startAddr);
                    startAddr += UIntPtr.Size;
                }
            }
            return startAddr;
        }

        [Inline]
        internal static UIntPtr AlignedObjectPtr(UIntPtr startAddr,
                                                 uint alignment)
        {
            if (alignment > UIntPtr.Size) {
                uint alignmentMask = alignment - 1;
                int offset = PostHeader.Size;
                while (((startAddr + offset) & alignmentMask) != 0) {
                    Allocator.WriteAlignment(startAddr- PreHeader.Size);
                    startAddr += UIntPtr.Size;
                }
            }
            return startAddr;
        }

        private static UIntPtr ALIGNMENT_TOKEN
        {
            [Inline]
            [NoHeapAllocation]
            get { return ~((UIntPtr)3u); }
        }

        // REVIEW: Consider moving to BartokObject --Bjarne
        [NoHeapAllocation]
        internal static unsafe
        UIntPtr * GetObjectVTableAddress(UIntPtr potentialObject)
        {
            UIntPtr vtableAddr = potentialObject + Magic.OffsetOfVTable;
            return (UIntPtr *) vtableAddr;
        }

        // REVIEW: Consider moving to BartokObject --Bjarne
        [NoHeapAllocation]
        internal static unsafe UIntPtr GetObjectVTable(UIntPtr potentialObject)
        {
            return *GetObjectVTableAddress(potentialObject);
        }

        // REVIEW: Consider moving to BartokObject --Bjarne
        [NoHeapAllocation]
        internal static unsafe void SetObjectVTable(UIntPtr potentialObject,
                                                    UIntPtr vtable)
        {
            *GetObjectVTableAddress(potentialObject) = vtable;
        }

        // REVIEW: Consider moving to BartokObject --Bjarne
        [NoHeapAllocation]
        internal static bool IsZeroVTable(UIntPtr addr)
        {
            return GetObjectVTable(addr) == UIntPtr.Zero;
        }

    }

}
