private static UIntPtr FirstPtrFromInteriorTable(UIntPtr c)
        {
            UIntPtr cardAddr     = CardTable.CardAddr(c);
            UIntPtr nextCardAddr = CardTable.NextCardAddr(c);
            UIntPtr page         = PageTable.Page(cardAddr);
            UIntPtr pageAddr     = PageTable.PageAddr(page);
            UIntPtr currAddr;

            if (page == 0)
            {
                currAddr = PtrToNextObject(pageAddr,
                                           (UIntPtr)PreHeader.Size, nextCardAddr);
            }
            else
            {
                short offset = PageTable.Extra(page);
                currAddr = UIntPtr.Zero;
                if (offset != InteriorPtrTable.OFFSET_NO_DATA)
                {
                    currAddr = pageAddr + (offset - InteriorPtrTable.OFFSET_SKEW);
                }

                // In general, we expect currAddr <= cardAddr. Or in the extreme
                // case, when the object starts from the page boundary,
                // currAddr - Object.HEADER_BYTES <= cardAddr. The contrary
                // cases has to be handled by searching previous pages.

                if (currAddr == UIntPtr.Zero ||
                    (currAddr > cardAddr &&
                     currAddr - PreHeader.Size > cardAddr))
                {
                    // look from previous pages, in case that an object on
                    // them spans to the current page. In that case, we should
                    // should use that object's ptr.

                    currAddr = InteriorPtrTable.Last(page - 1);

                    // Usually, Last() returns a pointer before or at the page
                    // boundary. However, there is one exception: when an object
                    // exactly fits to the last byte of the previous page, and the next
                    // object starts right from the page boundary (the first byte of
                    // the next page), then the pointer to this next object is returned.
                    // Example found: objPtr =3d09fa8, size=60, pageboundary=
                    // 3d0a000, next objPtr=3d0a008. Then returned pointer is
                    // 3d0a008, which is beyond the page boundary.

                    VTable.Assert(currAddr <= pageAddr ||
                                  currAddr - PreHeader.Size <= pageAddr,
                                  "object is expected before page or right at the beginning of it");
                }
            }
            VTable.Assert(currAddr < nextCardAddr, "object is expected before next card");

            while (currAddr < nextCardAddr)
            {
                if (Allocator.IsAlignment(currAddr))
                {
                    currAddr += UIntPtr.Size;
                }
                else if (BumpAllocator.IsUnusedSpace(currAddr))
                {
                    currAddr = PageTable.PagePad(currAddr) + PreHeader.Size;
                }
                else
                {
                    UIntPtr size = InteriorPtrTable.ObjectSize(currAddr);
                    if (currAddr + size - PreHeader.Size > cardAddr)
                    {
                        return(currAddr);
                    }
                    currAddr += size;
                }
            }
            VTable.Assert(false, "No obj ptr found by looking at interior table");
            return(UIntPtr.Zero);
        }
        internal static UIntPtr FirstObjPtrWithFieldInCard(UIntPtr c)
        {
            VTable.Assert(CardTable.IsValidCardNo(c), "Not valid card no");
            VTable.Assert(CardTable.IsMyLiveGcCard(c), "Not my live GC card");

            UIntPtr cardAddr     = CardTable.CardAddr(c);
            UIntPtr nextCardAddr = CardTable.NextCardAddr(c);

            // Scan backward. May go to zombie areas

            UIntPtr cardBefore = c - 1;

            while (cardBefore >= CardTable.FirstCardNo &&
                   OffsetTable.NoObjectPtrToTheCard(cardBefore) &&
                   CardTable.IsMyGcCard(cardBefore))
            {
                cardBefore--;
            }

            UIntPtr ptr;

            if (cardBefore < CardTable.FirstCardNo ||
                !CardTable.IsMyGcCard(cardBefore))
            {
                // this case happens when c is the first live card that has an object.

                VTable.Assert(CardTable.IsMyGcCard(cardBefore + 1),
                              "The next card must be a GC card");
                VTable.Assert(CardTable.CardAddr(cardBefore + 1) ==
                              CardTable.PageAddrOfCard(cardBefore + 1),
                              "The next card must be at the boundary of a page");

                UIntPtr pageAddr = CardTable.PageAddrOfCard(cardBefore + 1);
                ptr = PtrToNextObject(pageAddr, (UIntPtr)PreHeader.Size, nextCardAddr);
            }
            else
            {
                VTable.Assert(!OffsetTable.NoObjectPtrToTheCard(cardBefore),
                              "A card with an object must have been found");
                ptr = CardTable.CardAddr(cardBefore) +
                      OffsetTable.GetOffset(cardBefore);
            }
            VTable.Assert(ptr < nextCardAddr, "No objptr in this card");

            // Scan forward. Update offset table in the course.

            UIntPtr currentPtr = ptr;

            while (currentPtr < cardAddr)
            {
                UIntPtr size = OffsetTable.ObjectSize(currentPtr);
                if (currentPtr + size - PreHeader.Size > cardAddr)
                {
                    VTable.Assert(CardTable.CardGeneration(CardTable.CardNo(currentPtr)) ==
                                  CardTable.CardGeneration(c),
                                  "The whole object must be in the same generation");
                    break;
                }
                else
                {
                    ptr        = currentPtr;
                    currentPtr = PtrToNextObject(currentPtr, size, nextCardAddr);
                    VTable.Assert(CardTable.CardNo(ptr) <= CardTable.CardNo(currentPtr),
                                  "Previous ptr should be before current ptr");
                    if (CardTable.CardNo(ptr) < CardTable.CardNo(currentPtr))
                    {
                        UIntPtr ptrC    = CardTable.CardNo(ptr);
                        UIntPtr offsetC = ptr - CardTable.CardAddr(ptrC);
                        if (offsetC > GetOffset(ptrC))
                        {
                            SetOffset(ptrC, offsetC);
                        }
                    }
                }
            }
            VTable.Assert(currentPtr != UIntPtr.Zero, "current ptr is zero");
            VTable.Assert(currentPtr < nextCardAddr, "No objptr found in this card");

#if DEBUG_OFFSETTABLE
            UIntPtr temp = OffsetTable.FirstPtrFromInteriorTable(c);
            if (temp != currentPtr)
            {
                VTable.DebugPrint("Found ptr ({0:x8}) inconsistent with first ptr from interior table({1:x8})\n",
                                  __arglist(currentPtr, temp));
                VTable.Assert(false);
            }
#endif

            return(currentPtr);
        }