//-*-c++-*--------------------------------------------------------------
//  nsrSpace.cpp
//   Space Resource methods
//
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//                  RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//                      HEWLETT-PACKARD COMPANY
//                      3000 Hanover St.
//                      Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.
//
//  CLASSES:
//   ISOSpaceResource
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfCore/ss/nsrSpace.cpp,v $
// $Revision: 1.37 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/26 00:21:20 $

#include "nsrLib.h"
#include <assert.h>

//-----------------------------------------------------------------------------
// METHOD:  LELSpaceResource::takeLogExtent()
//
// ARGS:
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void 
NSR::LELSpaceResource::takeLogExtent( LogExtent& lext,
                                      UINT32 bc,
                                      Error& err )
{
    spacePool_.extractLogExtentFromHead(lext, bc, err);
    if (err)
        return;
    lext.infoLen(bc,err);
}

//-----------------------------------------------------------------------------
// METHOD:  LELSpaceResource::takeLogExtentList()
//
// ARGS:
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void 
NSR::LELSpaceResource::takeLogExtentList( LogExtentList& list,
                                          UINT64 bc,
                                          Error& err )
{
    spacePool_.extractLogExtentListFromHead(list, bc, err); 
    if (err)
        return;
    list.infoLen(bc,err);
}

//-----------------------------------------------------------------------------
// METHOD:  LELSpaceResource::putLogExtent()
//
// Add this log extent to our list.
//
// NOTE: Because we are a SpaceResource, (and we do not track any logical 
//   ordering of extents), then we want to insert this extent ONLY at 
//   that location ordered by startBlock(), so that it will coalesce
//   (if possible) with other extents in the list.  Because of this logic,
//   THIS SHOULD BE THE ONLY FUNCTION USED TO ADD EXTENTS TO THE LIST.
//  
// ARGS:
//
//  const LogExtent &  lext  IN  The extent to add to the resource.
//  const LogExtent &  lext OUT  The error if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void 
NSR::LELSpaceResource::putLogExtent( const LogExtent& lext,
                                     Error & err )
{
    // First, error out if not allowed in this SR.
    if ( err = validateExtent(lext) )
        return;

    // Create a block-multiple version to put into the SR.
    LogExtent bmlext(lext);
    bmlext.infoLen(lext.blockMultLen(),err);
    if (err)
        return;
    
    // Go through our list from the beginning.  Append at the end of the
    //   list, or before any extent with a start block before ours.
    // ISSUE: putLogExtent : Does NOT check on error values after each call!
    UINT64 num = spacePool_.numExtents();
    UINT64 i;
    LogExtent * lep = NULL;
    for(i = 0; i< num; i++) {
        // For each extent in our space pool.
        lep = spacePool_.at(i,err);
        if (err) {
            return;
        }
        if(lep->startBlock() > bmlext.startBlock()) {
            // If their start block should come AFTER our start block, 
            //   then we must insert before them.
            spacePool_.insertAt(bmlext, i, err);
            break;  // We inserted it.
        }
    }
    if(i == num) {
        // If we did not force an insert, then we are at the bottom
        //   of the list.  This extent gets appended.
        spacePool_.append(bmlext, err);
    }
    return;
}

//-----------------------------------------------------------------------------
// METHOD:  LELSpaceResource::putLotExtentList()
//
// ARGS:
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void 
NSR::LELSpaceResource::putLogExtentList( const LogExtentList& list,
                                         Error & err )
{
    const LogExtent * lep = NULL;
    UINT64 num = list.numExtents();
    for(UINT64 i=0; i< num; i++) {
        // For each extent in the list we add.
        lep = list.at(i,err);
        if (err) {
            return;
        }
        putLogExtent(*lep, err);
        if (err) {
            return;
        }
    }
}

//-----------------------------------------------------------------------------
// METHOD:  LELSpaceResource::takeLogExtentFromTail()
//
// ARGS:
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void 
NSR::LELSpaceResource::takeLogExtentFromTail( LogExtent& lext,
                                              UINT32 bc,
                                              Error& err )
{
    spacePool_.extractLogExtentFromTail(lext, bc, err);  
}

//-----------------------------------------------------------------------------
// METHOD:  LELSpaceResource::takeLogExtentListFromTail()
//
// ARGS:
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void 
NSR::LELSpaceResource::takeLogExtentListFromTail( LogExtentList& list,
                                                  UINT64 bc,
                                                  Error& err )
{
    spacePool_.extractLogExtentListFromTail(list, bc, err); 
}

// TableSpaceResource stuff

//----------------------------------------------------------------------
// METHOD: TableSpaceResource::initWithNewSpace  --DVM
//      
//  Initializes the object based upon the supplied space information.
//
// ARGS:
//  LogExtentList & initialSpace IN  Space for the ISO on-disk
//                                   structure is first taken from the
//                                   head, and then the tail is placed
//                                   into the SpaceResource. When this
//                                   exits, the initialSpace should be
//                                   empty.
//  Error &         err  OUT Returns any errors.
//
// RETURNS:
//  none.
// 
// PRE-CONDITIONS:
//  none.
// 
// POST-CONDITIONS:
//  Object has initialized its internal representation of free space,
//  and is ready for put() or take() calls. A syncToDisk() should
//  still be called to make sure that the disk image exactly matches
//  the internal representation.
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  Note that this does NOT allow the user to change the
//  logicalVolume, partition, or the SRType. These may only be changed
//  upon construction or on a fromDisk().
//
//----------------------------------------------------------------------
void 
NSR::TableSpaceResource::initWithNewSpace(LogExtentList & initialSpace)
{
    // Exit if initialSpace is in error_or.
    if ( error_ = initialSpace.error() ) {
        return;
    }

    // Extract room for the descriptor
    LogExtent spaceForDesc;
    if ( error_ = spaceForDesc.error() ) {
        return;
    }
    initialSpace.extractLogExtentFromHead(spaceForDesc,
                                          descBytes(partp_),
                                          error_);
    if ( error_ ) {
        return;
    }
    
    // Save off spaceForDesc in Partition.
    if (spaceType() == srSpace_ErasedOnly) {
        partp_->erasedSpaceTblExt(spaceForDesc);
    } else {
        partp_->unerasedSpaceTblExt(spaceForDesc);
    }

    // Clear out any current free space.        
    spacePool_.clear();

    // Finally, put all of the space into the SR.
    putLogExtentList(initialSpace, error_);

    // NOTE this is ready to use, even though no descriptor has been
    // written. The user needs to manually call syncToDisk().

    return;
}

//----------------------------------------------------------------------
// METHOD: TableSpaceResource::initFromDisk              --CSB
//      
//  Initializes the object to match the free space represented on disk.
//
// ARGS:
//  Error & OUT  returns an error if any encountered.
//
// RETURNS:
//  none.
// 
// PRE-CONDITIONS:
//  none.
// 
// POST-CONDITIONS:
//  Object has read in the free space from the surface, and is ready
//  for put() or take() calls.
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  We must use the AEDWriter to read any overflow AEDs, and we
//  use the UnallocSpaceEntry to read descriptor information.
//
//----------------------------------------------------------------------
void 
NSR::TableSpaceResource::initFromDisk(void)
{
    // Read the USE from the surface.  Then, instantiate an AEDWriter
    //   to read the remaining (overflow) AEDs from the surface.
    
    // First, erase any data we have.  We are populating from the surface.
    spacePool_.clear();           // Clear out any current free space
    error_ = error_None;             // Success is default
    UnallocatedSpaceEntry use(0); // Get a USE to fill in.

    // Find our extent.
    LogExtent descExt;
    if(spaceType() == srSpace_ErasedOnly) {
        descExt = partp_->erasedSpaceTblExt();
    } else /* if(srSpace_ == srSpace_UnerasedOnly) */ {
        descExt = partp_->unerasedSpaceTblExt();
    }

    // Read the USE from the surface.
    ByteArray buffer(descExt.infoLen());

    descExt.read(buffer, error_);
    if(!error_) {
        // If we could read the block from the surface.

        // Get it from the buffer.
        use.fromISO(buffer, descExt.startBlock());

        if(! (error_ = use.error())) {
            // If we could read the USE from the buffer.

            // Populate our space pool from the list from the surface.
            LogExtentList emptyBodyEL(*partp_->logVolumep());
            LogExtentList metaDataEL(*partp_->logVolumep());

            // Instantiate an AEDWriter to follow this ADTable
            //   across all AEDs and populate our list.

            AEDWriter aedWriter;
            aedWriter.fromDisk(descExt,
                               use.adTable(),
                               emptyBodyEL,
                               0, // First list length.
                               spacePool_,
                               metaDataEL,
                               error_);

        }
    }
}

//----------------------------------------------------------------------
// METHOD: TableSpaceResource::syncToDisk        --CSB
//      
//  Write this TableSpaceResource to the surface.
//
//  Space for the metadata is allocated from the space resource itself.
//
// ARGS:
//  Error &         err    OUT     Set to appropriate error.
//
// RETURNS:
//  none.
// 
// PRE-CONDITIONS:
//  List_preconditions  (E.g., assumes)
// 
// POST-CONDITIONS:
//  List_postconditions (E.g., changes to class data members)
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  This is implemented in the ISOSpaceResource class, and inherited.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// METHOD: TableSpaceResource::syncToDiskUsingSR         --CSB
//      
//  Write this TableSpaceResource to the surface.
//
//  The space for the metadata is allocated from the supplied SR.
//
// ARGS:
//  SpaceResource & mdsr   IN/OUT  Supplies disk space if needed.
//  Error &         err    OUT     Set to appropriate error.
//
// RETURNS:
//  none.
// 
// PRE-CONDITIONS:
//  List_preconditions  (E.g., assumes)
// 
// POST-CONDITIONS:
//  List_postconditions (E.g., changes to class data members)
//
// ERRORS:
//  error_
//  error_
//  error_
//
// NOTES:
//  We must use the AEDWriter to write any overflow AEDs, and we
//  use the UnallocSpaceEntry to write descriptor information.
//
//----------------------------------------------------------------------
void
NSR::TableSpaceResource::syncToDiskUsingSR(SpaceResource & mdsr,
                                           Error & err)
{
    // We want to instantiate an UnallocatedSpaceEntry and populate
    //   it with as many LogExtents as possible.  Then, we want
    //   to write all the remaining LogExtents in overflow AEDs with
    //   the AEDWriter.  Then, we want to flush the USE to the surface.

     // Create our USE.
    UnallocatedSpaceEntry use(0);

    // Populate the USE with all relevant data.
    // !!! We don't populate anything???
    // Do we populate use.lenAllocDesc?

     // Re-size the USE to hold the maximum number of ADTEnts.
    use.numADTEnts(blockSize(), use.maxADTEnts(blockSize()));

    // Now, write out the remaining ADTEnts in our overflow AEDs to
    //   the surface.
    LogExtentList metaDataEL(logVolume());
    LogExtentList emptyBodyEL(logVolume());
    AEDWriter aedWriter;
    err = error_None;

    LogExtent descExt;
    if(spaceType() == srSpace_ErasedOnly) {
        descExt = partp_->erasedSpaceTblExt();
    } else /* if(srSpace_ == srSpace_UnerasedOnly) */ {
        descExt = partp_->unerasedSpaceTblExt();
    }

    aedWriter.toDisk(descExt,
                     use.adTable(),
                     emptyBodyEL,
                     spacePool_,
                     metaDataEL,
                     mdsr,
                     err);

    // At this point, we wrote the overflow AEDs, if necessary.
    // We can (optionally) shrink the size of the use.adTable() so
    //   the un-used entries are not allocated (but that shouldn't
    //   matter anyway since the entire sector is lost on the surface
    //   anyway).

     //   Write the USE.
    if(!err) {
        // If we had no problems writing the overflow AEDs.
        ByteArray buffer(descExt.infoLen());

        use.toISO(buffer, descExt.startBlock());

        if(! (err = use.error())) {
            // If we could write the block in an ISO form.
            descExt.write(buffer, err);
        }
    }
}

// This stuff for test purposes only!
#if defined TEST_SR_BITMAP
extern void GetBitmapExtent(NSR::LogExtent& clLE);
extern void ReadBitmapImage(const NSR::LogExtent& clLE,
                            ByteArrayRef& clBARef,
                            Error& err);
extern void WriteBitmapImage(const NSR::LogExtent& clLE,
                             const ByteArrayRef& clBARef,
                             Error& err);
#endif

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapSpaceResource()                 -GKG
//
// Construct a BitmapSpaceResource from an image on disk.
//
// ARGS:       none
//  Partition& clPart                 IN   The partition containing this
//                                         BitmapSpaceResource.
//  STSpaceType nType                 IN   The type of this space resource.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapSpaceResource(Partition& clPart,
                                              SRSpaceType nType):
        ISOSpaceResource(clPart, nType)
{
    bmMap_ = NULL;
    bmPages_ = NULL;
    bufferCache_ = NULL;
    auditTrail_ = NULL;

    initFromDisk();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapSpaceResource()                 -GKG
//
// Construct a BitmapSpaceResource algoritmically from initial condition data.
//
// ARGS:       none
//  Partition& clPart                 IN   The partition containing this
//                                         BitmapSpaceResource.
//  STSpaceType nType                 IN   The type of this space resource.
//  LogExtentList& clInitSpace        IN   The initial free space list.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapSpaceResource(Partition& clPart,
                                              SRSpaceType nType,
                                              LogExtentList& clInitSpace):
        ISOSpaceResource(clPart, nType)
{
    bmMap_ = NULL;
    bmPages_ = NULL;
    bufferCache_ = NULL;
    auditTrail_ = NULL;

    initWithNewSpace(clInitSpace);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::~BitmapSpaceResource()                -GKG
//
// All we really need to do is release the heap based objects.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::~BitmapSpaceResource()
{
    free();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::findFirstFree()                       -GKG
//
// Finds the first page containing a free block.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::findFirstFree(UINT32 nStartPage)
{
    // If there are no free blocks, what's the point?
    if (freeBlocks_ == 0)
        return;

    // Loop through each page, until we find one with a free block.
    for (pageFirstFree_ = nStartPage; pageFirstFree_ < bmMap_->numPages(); pageFirstFree_++)
        if (bmPages_[pageFirstFree_].freeBlocks())
            break;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::findLargestExtent()                   -GKG
//
// Finds the largest number of contiguous free blocks in the entire bitmap.
//
// ARGS:
//  UINT32 nPage                      IN   The beginning page number.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::findLargestExtent()
{
    // We begin by assuming its zero.
    largestExtent_ = 0;
    if (freeBlocks_ == 0)
        return;

    // Loop through the pages checking the largest extent in each page and
    // the boundary extent for that page.
    UINT32 nCount;
    for (nCount = pageFirstFree_; nCount < bmMap_->numPages(); nCount++)
    {
        // If the largest extent of the current page is larger than the current
        // largest extent, then it is the largest extent. (?)
        if (bmPages_[nCount].largestExtent() > largestExtent_)
        {
            largestExtent_ = bmPages_[nCount].largestExtent();
            locLargestExtent_ = bmMap_->pageBitAddr(nCount, bmPages_[nCount].locLargestExtent());
        }

        // Now, we check the extent crossing the ending page boundary of the
        // current page, to see if it is larger than the current largest extent.
        UINT32 nExtentLength = findBoundaryExtent(nCount);
        if (nExtentLength)
        {
            UINT32 nStartBlock = bmMap_->pageBitAddr(nCount, bmPages_[nCount].totalBlocks() - bmPages_[nCount].pageEndExtent());

            // If its larger, make it the current largest extent.
            if (nExtentLength > largestExtent_)
            {
                largestExtent_ = nExtentLength;
                locLargestExtent_ = nStartBlock;
            }

            // The boundary extent could span multiple pages, so we don't need to
            // check the pages in between.
            nCount = bmMap_->pageNum(nStartBlock + nExtentLength) - 1;
        }
    }
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::findBoundaryExtent()                  -GKG
//
// Finds the number of contiguous free blocks beginning from the end of the
// specified page, continuing across page boundaries until the first allocated
// block or the end of the bitmap is encountered.
//
// ARGS:
//  UINT32 nPage                      IN   The beginning page number.
//
// RETURNS:
//  UINT32                                 The boundary extent length (in
//                                         blocks).
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::UINT32
NSR::BitmapSpaceResource::findBoundaryExtent(UINT32 nPage) const
{
    if (nPage >= bmMap_->numPages() - 1)
        return 0;

    // If this page has no free blocks adjoining the end page boundary, then
    // the boundary extent is zero.
    UINT32 nExtLength = bmPages_[nPage++].pageEndExtent();
    if (nExtLength == 0)
        return 0;

    // If the next page has no free blocks adjoining the beginning page boundary,
    // then the boundary extent is zero.
    UINT32 nTempLength = bmPages_[nPage].pageBeginExtent();
    if (nTempLength == 0)
        return 0;

    // Accumulate the extent length page-by-page, until we find a page with
    // a free block or we have processed the last page.
    nExtLength += nTempLength;
    while (bmPages_[nPage++].pageFree() && nPage < bmMap_->numPages())
        nExtLength += bmPages_[nPage].pageBeginExtent();

    return nExtLength;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::popAuditTrail()                       -GKG
//
// Remove the first audit trail entry and advance the following one to be the
// head of the list.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::popAuditTrail()
{
    if (auditTrail_)
    {
        AuditTrailNode* pNode = auditTrail_;
        // Make the next one the head of the list.
        auditTrail_ = pNode->getNext();
        delete pNode;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::disasterRecovery()                    -GKG
//
// Reads a bitmap from disk and initializes associated meta-data.
//
// ARGS:       none
//  Boolean nTake                     IN   Flag to determine if failure is "put"
//                                         or "take" operation.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::disasterRecovery(Boolean nTake)
{
    Error err;

    while (auditTrail_)
    {
        if (nTake)
            bmPages_[auditTrail_->getPage()].putExtent(*auditTrail_, err);
        else
            bmPages_[auditTrail_->getPage()].takeExtent(*auditTrail_, err);

        if (err && !err_)
            err_ = err;

        popAuditTrail();
    }

    if (!err_)
        findBitmapData();

    bufferCache_->syncCache(err);
    if (err && !err_)
        err_ = err;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::free()                                -GKG
//
// Releases all heap allocated objects associated with this class.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::free()
{
    // Release objects
    clearAuditTrail();
    delete bufferCache_;
    delete [] bmPages_;
    delete bmMap_;

    // Clear state variables.
    bufferCache_ = NULL;
    bmPages_ = NULL;
    bmMap_ = NULL;
    err_ = error_SR_Incomplete;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::takeLogExtent()                       -GKG
//
// Allocate a contiguous block of space from the space resource.
//
// ARGS:
//  LogExtent& clLogExt     OUT  The allocated extent.
//  UINT32 nLength          IN   The number of bytes to allocate.
//  Error& err              OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::takeLogExtent(LogExtent& clLogExt,
                                        UINT32 nLength,
                                        Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = err_;
    if (err)
        return;

    // Make sure we've got a valid extent length.
    if (nLength > partp_->maxExtLen())
    {
        err = error_SR_Bad_extent_length;
        return;
    }

    clearAuditTrail();

    // Test to be sure we can satisfy this request.
    UINT32 nBlocks = bmMap_->bytesToBlocks(nLength);
    if (nBlocks > largestExtent_)
    {
        err = error_SR_Insufficient_space;
        return;
    }

    Extent clExt;
    UINT32 nPage;

    // Loop through bitmap pages looking for the first that can satisfy the
    // request.
    for (nPage = pageFirstFree_; nPage < bmMap_->numPages(); nPage++)
    {
        if (nBlocks <= bmPages_[nPage].largestExtent())
        {
            // The request can be satisfied fully with the current page.
            bmPages_[nPage].takeExtent(clExt, nBlocks, err);
            if (err)
            {
                err_ = err;
                disasterRecovery(True);
                return;
            }

            // Append this allocation onto the audit trail.
            pushAuditTrail(clExt, nPage, err);
            if (err)
            {
                disasterRecovery(True);
                return;
            }

            // Get the true starting block of the allocated extent.
            clExt.location = bmMap_->pageBitAddr(nPage, clExt.location);
            nBlocks = 0;

            // Update the disk image.
            bmPages_[nPage].syncPage(err);
            if (err)
            {
                err_ = err;
                disasterRecovery(True);
                return;
            }

            break;
        }
        else
        {
            // The request cannot be satisfied fully with the current page, so
            // check to see if the extent spanning the ending boundary of the
            // current page can.
            if (nBlocks <= findBoundaryExtent(nPage))
            {
                // The boundary extent CAN satisfy this request.
                // Only take the last extent from the current page.
                bmPages_[nPage].takeEndExtent(clExt, nBlocks, err);
                if (err)
                {
                    err_ = err;
                    disasterRecovery(True);
                    return;
                }

                // Put the extent on the audit trail.
                pushAuditTrail(clExt, nPage, err);
                if (err)
                {
                    disasterRecovery(True);
                    return;
                }

                // Adjust the remaining bytes and update the disk image.
                nBlocks -= clExt.length;
                bmPages_[nPage].syncPage(err);
                if (err)
                {
                    err_ = err;
                    disasterRecovery(True);
                    return;
                }

                // Get the true starting block of the allocated extent.
                clExt.location = bmMap_->pageBitAddr(nPage++, clExt.location);

                // Loop through the following pages until the request is satisfied.
                while (nBlocks && nPage < bmMap_->numPages())
                {
                    Extent clTempExt(0);
                    // Determine how much of the remaining bytes can be taken from
                    // this page.
                    UINT32 nExtLength = (nBlocks > bmPages_[nPage].totalBlocks()) ? bmPages_[nPage].totalBlocks() : nBlocks;

                    // Take only from the beginning extent of this page.
                    bmPages_[nPage].takeBeginExtent(clTempExt, nExtLength, err);
                    if (err)
                    {
                        err_ = err;
                        disasterRecovery(True);
                        return;
                    }

                    // Put the extent on the audit trail.
                    pushAuditTrail(clTempExt, nPage, err);
                    if (err)
                    {
                        disasterRecovery(True);
                        return;
                    }

                    // Adjust the amount of bytes remaining.
                    nBlocks -= nExtLength;
                    clExt.length += nExtLength;
                    if (nBlocks && clTempExt.length < bmPages_[nPage].totalBlocks())
                    {
                        // Uh oh! The meta-data lied! Big error!
                        err_ = err = error_SR_Bitmap_corrupt;
                        disasterRecovery(True);
                        return;
                    }

                    // Update the image on disk.
                    bmPages_[nPage++].syncPage(err);
                    if (err)
                    {
                        err_ = err;
                        disasterRecovery(True);
                        return;
                    }
                }

                break;
            }
        }
    }

    if (nBlocks)
    {
        // Hmmm! We broke out of the allocation loop but still have unsatisfied
        // space.  This is bad.
        err_ = err = error_SR_Bitmap_corrupt;
        disasterRecovery(True);
        return;
    }

    // Update the number of free blocks in the space resource.
    freeBlocks_ -= clExt.length;

    // Find the first available space.
    if (bmPages_[pageFirstFree_].freeBlocks() == 0)
        findFirstFree(bmMap_->pageNum(clExt.location + clExt.length));

    // Find the largest available extent.
    if (clExt.location >= locLargestExtent_ && clExt.location < locLargestExtent_ + largestExtent_)
        findLargestExtent();

    // Initialize the return extent.
    clLogExt.reinit(partp_, clExt.location, nLength, err);
    clLogExt.extentType((spaceType() == srSpace_ErasedOnly) ? Erased : Unerased);
#if defined TEST_SR_BITMAP
    // If we are testing, we don't care about partition size.
    if (err == error_LE_beyond_partition)
        err = error_None;
#endif
    if (err)
    {
        err_ = err;
        disasterRecovery(True);
        return;
    }

    clearAuditTrail();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::takeLogExtentList()                   -GKG
//
// Allocate a potential disjoint set of extents from the space resource.
//
// ARGS:
//  LogExtentList& clLEList OUT  The allocated extent list.
//  UINT32 nLength          IN   The number of bytes to allocate.
//  Error& err              OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::takeLogExtentList(LogExtentList& clLEList,
                                            UINT64 nLength,
                                            Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = err_;
    if (err)
        return;

    clearAuditTrail();

    // Test to be sure we can satisfy this request.
    UINT32 nBlocks = bmMap_->bytesToBlocks(nLength);
    if (nBlocks > freeBlocks_)
    {
        err = error_SR_Insufficient_space;
        return;
    }

    Extent clExt;
    Extent clContExt;
    clContExt.length = 0;
    Boolean nFindLargest = False;

    UINT32 nPage = pageFirstFree_;

    // Loop through the pages, allocating space until the request is satisfied.
    while (nBlocks && nPage < bmMap_->numPages())
    {
        // Make sure that we do not allocate a single extent that is too big.
        UINT32 nExtentBlocks = bmMap_->maxExtentBlocks();
        if (nBlocks < nExtentBlocks)
            nExtentBlocks = nBlocks;

        // Get successive extents from the current page until the request is
        // satisfied or the page is empty.
        while (nExtentBlocks && bmPages_[nPage].freeBlocks())
        {
            // Allocate from the first available extent.
            bmPages_[nPage].takeNextExtent(clExt, nExtentBlocks, err);
            if (err)
            {
                disasterRecovery(True);
                return;
            }

            // Put the extent on the audit trail.
            pushAuditTrail(clExt, nPage, err);
            if (err)
            {
                disasterRecovery(True);
                return;
            }

            if (clExt.length == 0)
            {
                // A zero length extent?  It better not happen.
                err_ = err = error_SR_Bitmap_corrupt;
                disasterRecovery(True);
                return;
            }

            // Adjust the remaining amount.
            nBlocks -= clExt.length;
            nExtentBlocks -= clExt.length;
            freeBlocks_ -= clExt.length;

            // This builds the current extent so it may cross page boundaries.
            if (clContExt.length == 0)
                clContExt.location = bmMap_->pageBitAddr(nPage, clExt.location);
            clContExt.length += clExt.length;

            // Complete the current extent.
            if (nExtentBlocks == 0 || !bmPages_[nPage].isEndExtent(clExt) || nPage + 1 == bmMap_->numPages() || bmPages_[nPage + 1].pageBeginExtent() == 0)
            {
                // Determine how many bytes should be in the extent info length.
                UINT64 nBytesThisExtent = bmMap_->blocksToBytes(clContExt.length);
                if (nBytesThisExtent > nLength)
                    nBytesThisExtent = nLength;

                // Determin how many bytes are left to go.
                nLength -= nBytesThisExtent;

                // Build the extent and append it to the list.
                LogExtent clLE(partp_, clContExt.location, nBytesThisExtent);
                clLE.extentType((spaceType() == srSpace_ErasedOnly) ? Erased : Unerased);
                clLEList.append(clLE, err);
                if (err)
                {
                    disasterRecovery(True);
                    return;
                }

                // Determine if we will need to find the largest extent when we
                // are done.
                if (clContExt.location == locLargestExtent_)
                    nFindLargest = True;

                // Set the extent length to zero to begin the next extent.
                clContExt.length = 0;
            }
        }

        // Update the page image on disk.
        bmPages_[nPage].syncPage(err);
        if (err)
        {
            err_ = err;
            disasterRecovery(True);
            return;
        }

        // Advance to the next page.
        if (nBlocks)
            nPage++;
    }

    if (nBlocks || clContExt.length)
    {
        // We broke out of the allocation loop and there the request is not
        // satisfied.  Very bad.
        err_ = err = error_SR_Bitmap_corrupt;
        disasterRecovery(True);
        return;
    }

    // Find the first available block.
    findFirstFree(nPage);

    // Find the largest run of contiguous blocks (if necessary).
    if (nFindLargest)
        findLargestExtent();

    clearAuditTrail();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::putLogExtent()                        -GKG
//
// Deallocate a single extent into the space resource.
//
// ARGS:
//  const LogExtent& clLogExt         IN   The extent to deallocate.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::putLogExtent(const LogExtent& clLogExt,
                                       Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = err_;
    if (err)
        return;

    // Make sure this is a valid extent.
    err = validateExtent(clLogExt);
    if (err)
        return;

    clearAuditTrail();

    // Determine what page the extent starts in, where it starts, and how long
    // it is.
    Extent clExt;
    clExt.location = bmMap_->pageBitOffset(clLogExt.startBlock());
    UINT32 nPage = bmMap_->pageNum(clLogExt.startBlock());
    UINT32 nBlocks = clLogExt.numBlocks();

    // Check to make sure the extent isn't past the end of the last page.
    if (nPage == bmMap_->numPages() - 1 && clExt.location >= bmPages_[nPage].totalBlocks())
    {
        err = error_SR_Cannot_free_space;
        return;
    }

    // Loop through the pages deallocating blocks until the entire extent is
    // accunted for.
    while (nBlocks)
    {
        // If this happens, the supplied extent overran the end of the partition.
        if (nPage >= bmMap_->numPages())
        {
            err = error_SR_Cannot_free_space;
            disasterRecovery(False);
            return;
        }

        // Determine how much of the remaining extent fits in the current page.
        clExt.length = (clExt.location + nBlocks > bmPages_[nPage].totalBlocks()) ? bmPages_[nPage].totalBlocks() - clExt.location : nBlocks;

        // Free the applicable portion to the page.
        bmPages_[nPage].putExtent(clExt, err);
        if (err)
        {
            // If the supplied extent was invalid the error is recoverable.
            if (err != error_SR_Cannot_free_space)
                err_ = err;

            disasterRecovery(False);
            return;
        }

        // Put the extent on the audit trail.
        pushAuditTrail(clExt, nPage, err);
        if (err)
        {
            err_ = err;
            disasterRecovery(False);
            return;
        }

        // Update the page image on disk.
        bmPages_[nPage].syncPage(err);
        if (err)
        {
            err_ = err;
            disasterRecovery(False);
            return;
        }

        // Update the number of free blocks and amount remaining.
        nBlocks -= clExt.length;
        freeBlocks_ += clExt.length;
        clExt.location = 0;

        // Advance to the next page.
        nPage++;
    }

    // Find the page with the first available block (if necessary).
    nPage = bmMap_->pageNum(clLogExt.startBlock());
    if (nPage < pageFirstFree_)
        pageFirstFree_ = nPage;

    // Find the largest extent.
    if (largestExtent_ == 0)
    {
        // There was no largest extent before, so this extent must be the largest.
        largestExtent_ = clLogExt.numBlocks();
        locLargestExtent_ = clLogExt.startBlock();
    }
    else
    {
        // Get the location of the largest extent in the current page.
        UINT32 nExtLocation = bmMap_->pageBitAddr(nPage, bmPages_[nPage].locLargestExtent());
        UINT32 nExtLength = bmPages_[nPage].largestExtent();

        // Find the end boundary extent of the current page (unless the page is
        // completely unallocated).
        if (!bmPages_[nPage].pageFree())
        {
            nBlocks = findBoundaryExtent(nPage);
            if (nBlocks > nExtLength)
            {
                // The end boundary extent is the largest.
                nExtLocation = bmMap_->pageBitAddr(nPage, bmPages_[nPage].totalBlocks() - bmPages_[nPage].pageEndExtent());
                nExtLength = nBlocks;
            }
        }

        // Find the beginning boundary extent of the current page (which might
        // begin on some previos page).
        if (bmPages_[nPage].pageBeginExtent())
        {
            while (nPage && bmPages_[nPage - 1].pageEndExtent())
                if (!bmPages_[--nPage].pageFree())
                    break;

            nBlocks = findBoundaryExtent(nPage);
            if (nBlocks > nExtLength)
            {
                // The beginning boundary extent is the largest.
                nExtLocation = bmMap_->pageBitAddr(nPage, bmPages_[nPage].totalBlocks() - bmPages_[nPage].pageEndExtent());
                nExtLength = nBlocks;
            }
        }

        // See if the largest extent adjoining the current page is larger than the
        // current largest extent.
        if (nExtLength > largestExtent_)
        {
            largestExtent_ = nExtLength;
            locLargestExtent_ = nExtLocation;
        }
        else
            if (nExtLength == largestExtent_ && nExtLocation < locLargestExtent_)
                locLargestExtent_ = nExtLocation;
    }

    clearAuditTrail();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::putLogExtentList()                    -GKG
//
// Deallocate a list of extents into the space resource.
//
// ARGS:
//  const LogExtentList& clLEList     IN   The extent to deallocate.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::putLogExtentList(const LogExtentList& clLEList,
                                                Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = err_;
    if (err)
        return;

    clearAuditTrail();

    UINT32 nExtents = clLEList.numLogExtents();
    UINT32 nCurrEntry = 0;

    // Loop through the extents in the list, deallocating each one in succession.
    while (nCurrEntry < nExtents)
    {
        // Get the next extent.
        const LogExtent* pExtent = clLEList.at(nCurrEntry, err);
        if (err)
            return;

        // Make sure this extent is valid.
        err = validateExtent(*pExtent);
        if (err)
        {
            disasterRecovery(False);
            return;
        }

        // Determine what page the extent starts in, where it starts, and how long
        // it is.
        Extent clExt;
        clExt.location = bmMap_->pageBitOffset(pExtent->startBlock());
        UINT32 nPage = bmMap_->pageNum(pExtent->startBlock());
        UINT32 nBlocks = pExtent->numBlocks();

        // Check to make sure the extent isn't past the end of the last page.
        if (nPage == bmMap_->numPages() - 1 && clExt.location >= bmPages_[nPage].totalBlocks())
        {
            err = error_SR_Cannot_free_space;
            disasterRecovery(False);
            return;
        }

        // Loop throught the pages until the current extent is deallocated.
        while (nBlocks)
        {
            // If this happens, the supplied extent overran the end of the partition.
            if (nPage >= bmMap_->numPages())
            {
                err = error_SR_Cannot_free_space;
                disasterRecovery(False);
                return;
            }

            // Determine how much of the current extent fits in the current page.
            clExt.length = (clExt.location + nBlocks > bmPages_[nPage].totalBlocks()) ? bmPages_[nPage].totalBlocks() - clExt.location : nBlocks;

            // Deallocate the portion of the extent that fits in this page.
            bmPages_[nPage].putExtent(clExt, err);
            if (err)
            {
                // If the supplied extent was invalid the error is recoverable.
                if (err != error_SR_Cannot_free_space)
                    err_ = err;

                disasterRecovery(False);
                return;
            }

            // Put the extent on the audit trail.
            pushAuditTrail(clExt, nPage, err);
            if (err)
            {
                err_ = err;
                disasterRecovery(False);
                return;
            }

            // Adjust the number of free blocks and the amount left.
            nBlocks -= clExt.length;
            freeBlocks_ += clExt.length;
            clExt.location = 0;

            // Advance to the next page.
            nPage++;
        }

        // Find the starting page of the current extent (again).
        nPage = bmMap_->pageNum(pExtent->startBlock());

        // Adjust if it is the first page with available extents.
        if (nPage < pageFirstFree_)
            pageFirstFree_ = nPage;

        // Find the largest extent.
        if (largestExtent_ == 0)
        {
            // There was no largest extent before, so this extent must be the
            // largest.
            largestExtent_ = pExtent->numBlocks();
            locLargestExtent_ = pExtent->startBlock();
        }
        else
        {
            // Get the location of the largest extent in the current page.
            UINT32 nExtLocation = bmMap_->pageBitAddr(nPage, bmPages_[nPage].locLargestExtent());
            UINT32 nExtLength = bmPages_[nPage].largestExtent();

            // Find the end boundary extent of the current page (unless the page is
            // completely unallocated).
            if (!bmPages_[nPage].pageFree())
            {
                nBlocks = findBoundaryExtent(nPage);
                if (nBlocks > nExtLength)
                {
                    // The end boundary extent is the largest.
                    nExtLocation = bmMap_->pageBitAddr(nPage, bmPages_[nPage].totalBlocks() - bmPages_[nPage].pageEndExtent());
                    nExtLength = nBlocks;
                }
            }

            // Find the beginning boundary extent of the current page (which might
            // begin on some previos page).
            if (bmPages_[nPage].pageBeginExtent())
            {
                while (nPage && bmPages_[nPage - 1].pageEndExtent())
                    if (!bmPages_[--nPage].pageFree())
                        break;

                nBlocks = findBoundaryExtent(nPage);
                if (nBlocks > nExtLength)
                {
                    // The beginning boundary extent is the largest.
                    nExtLocation = bmMap_->pageBitAddr(nPage, bmPages_[nPage].totalBlocks() - bmPages_[nPage].pageEndExtent());
                    nExtLength = nBlocks;
                }
            }

            // See if the largest extent adjoining the current page is larger than
            // the current largest extent.
            if (nExtLength > largestExtent_)
            {
                largestExtent_ = nExtLength;
                locLargestExtent_ = nExtLocation;
            }
            else
                if (nExtLength == largestExtent_ && nExtLocation < locLargestExtent_)
                    locLargestExtent_ = nExtLocation;
        }

        // Advance to the next entry in the extent list.
        nCurrEntry++;
    }

    // Synchronize the cache.
    bufferCache_->syncCache(err);
    if (err)
    {
        err_ = err;
        disasterRecovery(False);
        return;
    }

    clearAuditTrail();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::initWithNewSpace()                    -GKG
//
// Reads a bitmap from disk and initializes associated meta-data.
//
// ARGS:       none
//  LogExtentList& clInitSpace        IN   List of initial space.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::initWithNewSpace(LogExtentList& clInitSpace)
{
    // Free any pre-existing structures.
    if (bmMap_ || bufferCache_ || bmPages_ || auditTrail_)
        free();

    freeBlocks_ = largestExtent_ = 0;

    // Make sure we have a partiton.
    if (!partp_)
    {
        err_ = error_SR_Bad_partition;
        return;
    }

    // Extract enough space for the bitmap image from the initial space list.
    LogExtent clLExt;
    clInitSpace.extractLogExtentFromHead(clLExt, descBytes(partp_), err_);
    if (err_)
        return;

    // Make sure we got a valid extent.
    if (!clLExt.infoLen())
    {
        err_ = error_SR_Bad_extent;
        return;
    }

    SpaceBitmapDesc clBitmapDesc(partp_->numBlocks());

    // Setup the PagedSpaceBitmapMap for this space resource.
    bmMap_ = new PagedSpaceBitmapMap(clBitmapDesc, clLExt.startBlock(), partp_->logVolumep()->blockSize());
    if (!bmMap_)
    {
        err_ = error_No_free_store;
        return;
    }

    // Make sure the header information is consistent.
    if (clBitmapDesc.numBits() > (clBitmapDesc.numBytes() << 3) || clBitmapDesc.numBytes() + bmMap_->startByteOffset() > clLExt.infoLen())
    {
        err_ = error_SR_Bad_descriptor;
        return;
    }

    // Allocate the BitmapPage object (one for each page).
    bmPages_ = new BitmapPage[bmMap_->numPages()];
    if (!bmPages_)
    {
        err_ = error_No_free_store;
        return;
    }

    // Create the buffer cache.
    bufferCache_ = new BitmapBufferCache(bmMap_->pageSize(), (bmMap_->numPages() < maxCachePages) ? bmMap_->numPages() : maxCachePages);
    if (!bufferCache_)
    {
        err_ = error_No_free_store;
        return;
    }

    // Iteratively initiaize each BitmapPage object.
    for (UINT32 nCount = 0; nCount < bmMap_->numPages(); nCount++)
    {
        bmPages_[nCount].initialize(this, nCount, True, err_);
        if (err_)
            return;
    }

    freeBlocks_ = largestExtent_ = 0;

#if defined TEST_SR_BITMAP
    err_ = error_SR_Debug_code;
#endif
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::initFromDisk()                        -GKG
//
// Reads a bitmap from disk and initializes associated meta-data.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::initFromDisk(void)
{
    // Free any pre-existing structures.
    if (bmMap_ || bufferCache_ || bmPages_ || auditTrail_)
        free();

    freeBlocks_ = largestExtent_ = 0;

    // Make sure we have a partiton.
    if (!partp_)
    {
        err_ = error_SR_Bad_partition;
        return;
    }

    LogExtent clLExt;

    // Get the logical extent for the space resource.
    switch (spaceType())
    {
    case srSpace_ErasedOnly:
#if defined TEST_SR_BITMAP
        GetBitmapExtent(clLExt);
#else
        clLExt = partp_->erasedSpaceBMapExt();
#endif
        break;
    case srSpace_UnerasedOnly:
#if defined TEST_SR_BITMAP
        GetBitmapExtent(clLExt);
#else
        clLExt = partp_->unerasedSpaceBMapExt();
#endif
        break;
    default:
        err_ = error_SR_Bad_type;
        return;
    }

    // Make sure we got a valid extent.
    if (!clLExt.infoLen())
    {
        err_ = error_SR_Bad_extent;
        return;
    }

    Error err;
    LogExtent clFirstBlock;

    // We need only the first logical block of the extent (to read the header).
    clFirstBlock.reinit(clLExt.partitionp(), clLExt.startBlock(), partp_->blockSize(), err);
    if (err)
    {
        err_ = err;
        return;
    }
    clFirstBlock.markRecorded();

    // Setup the block buffer.
    ByteArray clBlock(partp_->blockSize());
    if (clBlock.error())
    {
        err_ = clBlock.error();
        return;
    }
    ByteArrayRef clBlockBARef(clBlock);

    // Read the first block into the buffer.
#if defined TEST_SR_BITMAP
    ReadBitmapImage(clFirstBlock, clBlockBARef, err);
#else
    clFirstBlock.read(clBlockBARef, err);
#endif

    if (err)
    {
        err_ = error_SR_Bitmap_read;
        return;
    }

    SpaceBitmapDesc clBitmapDesc;

    // Unpack the descriptor header from the buffer.
    clBitmapDesc.fromISO(clBlock, clFirstBlock.startBlock());
    if (clBitmapDesc.error())
    {
        err_ = error_SR_Bad_descriptor;
        return;
    }

    // Setup the PagedSpaceBitmapMap for this space resource.
    bmMap_ = new PagedSpaceBitmapMap(clBitmapDesc, clLExt.startBlock(), partp_->logVolumep()->blockSize());
    if (!bmMap_)
    {
        err_ = error_No_free_store;
        return;
    }

    // Make sure the header information is consistent.
    if (clBitmapDesc.numBits() > (clBitmapDesc.numBytes() << 3) || clBitmapDesc.numBytes() + bmMap_->startByteOffset() > clLExt.infoLen())
    {
        err_ = error_SR_Bad_descriptor;
        return;
    }

    // Allocate the BitmapPage object (one for each page).
    bmPages_ = new BitmapPage[bmMap_->numPages()];
    if (!bmPages_)
    {
        err_ = error_No_free_store;
        return;
    }

    // Create the buffer cache.
    bufferCache_ = new BitmapBufferCache(bmMap_->pageSize(), (bmMap_->numPages() < maxCachePages) ? bmMap_->numPages() : maxCachePages);
    if (!bufferCache_)
    {
        err_ = error_No_free_store;
        return;
    }

    // Iteratively initiaize each BitmapPage object.
    for (UINT32 nCount = 0; nCount < bmMap_->numPages(); nCount++)
    {
        bmPages_[nCount].initialize(this, nCount, False, err_);
        if (err_)
            return;
    }

    // Calculate the meta-data for the space resource.
    findBitmapData();

#if defined TEST_SR_BITMAP
    err_ = error_SR_Debug_code;
#endif
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::BitmapBuffer()          -GKG
//
// Allocate the buffer array and initialize the state variables.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapBuffer::BitmapBuffer(UINT32 nPageSize):
        bufferArray_(nPageSize)
{
    err_ = error_None;
    bmPage_ = NULL;
    nextBuffer_ = NULL;

    // Make sure the buffer is the right size.
    if (bufferArray_.length() != nPageSize)
    {
        err_ = error_No_free_store;
        return;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::~BitmapBuffer()         -GKG
//
// Flush and unlink any linked bitmap pages.
//
// ARGS:       none
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapBuffer::~BitmapBuffer()
{
    // Throw this away since we can't do anything with it at destruction time.
    Error err;

    unlinkBuffer(err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::linkBuffer()            -GKG
//
// Establish a link to a BitmapPage object and read it into the buffer.  Unlink
// and flush the current linked page if necessary.
//
// ARGS:
//  BitmapPage* pPage                 IN   Pointer of page to link to.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBuffer::linkBuffer(BitmapPage* pPage,
                                                        Error& err)
{
    // If currently linked to a page, unlink and synchonize it.
    if (bmPage_)
        unlinkBuffer(err);

    if (err)
        return;

    // Get the extent for the new page.
    UINT32 nLength = pPage->pageBlockSize() * pPage->getPartition().logVolumep()->blockSize();
    LogExtent clLExt(&pPage->getPartition(), pPage->startBlock(), nLength);
    clLExt.markRecorded();

    // Read the page image from media.
    ByteArrayRef clBARef(bufferArray_, 0, nLength);
#if defined TEST_SR_BITMAP
    ReadBitmapImage(clLExt, clBARef, err);
#else
    clLExt.read(clBARef, err);
#endif
    if (err)
    {
        err_ = error_SR_Bitmap_read;
        return;
    }

    bmPage_ = pPage;
    bitSize_ = bmPage_->totalBlocks() + SpaceBitmapMap::bitNumber(pPage->startByteOffset());

    // Establish the cross-linkage.
    pPage->setPageBuffer(this);

    // Make sure no "dirty" blocks are marked.
    clearDirty();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::unlinkBuffer()          -GKG
//
// Break the link with the current BitmapPage, updating the media image if
// necessary.
//
// ARGS:
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBuffer::unlinkBuffer(Error& err)
{
    err = error_None;

    // If the is no linked page, our job is done.
    if (!bmPage_)
        return;

    // Update the disk image.
    syncBuffer(err);
    if (err)
        return;

    // Break the cross-linkage.
    bmPage_->setPageBuffer(NULL);
    bmPage_ = NULL;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::syncBuffer()            -GKG
//
// Updates any "dirty" blocks in the current page.
//
// ARGS:
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBuffer::syncBuffer(Error& err)
{
    err = error_None;

    // No work to do if there are no dirty blocks.
    if (!isDirty())
        return;

    // Find the extent from the first to the last dirty block an write them to
    // the disk.
    UINT32 nLength = (lastDirty_ - firstDirty_ + 1) * bmPage_->getPartition().logVolumep()->blockSize();
    LogExtent clLExt(&bmPage_->getPartition(), bmPage_->startBlock() + firstDirty_, nLength);
    ByteArrayRef clBARef(bufferArray_, firstDirty_ * bmPage_->getPartition().logVolumep()->blockSize(), nLength);
    clLExt.markRecorded();

#if defined TEST_SR_BITMAP
    WriteBitmapImage(clLExt, clBARef, err);
#else
    clLExt.write(clBARef, err);
#endif
    if (err)
    {
        err_ = error_SR_Bitmap_write;
        return;
    }

    // Mark all dirty blocks as cleaned.
    clearDirty();
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::clear()                 -GKG
//
// Initializes all bits in the page to zero, and places the bitmap header
// image if linked to page zero.
//
// ARGS:
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBuffer::clear(Error& err)
{
    // Zero out the buffer.
    bufferArray_.clear();

    // If this is page zero, we need to initialize the bitmap header.
    if (bmPage_->pageNumber() == 0)
    {
        // Build the header.
        SpaceBitmapDesc clHeader(bmPage_->getBitmapMap().numBits());
        if (clHeader.error())
        {
            err = error_SR_Bad_descriptor;
            return;
        }

        // Place the header image at the beginning of the buffer.
        clHeader.toISO(bufferArray_, 0);
    }

    err = error_None;

    return;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::clearBits()             -GKG
//
// Clear a range of bits in the buffer.
//
// ARGS:
//  UINT32 nStart                     IN   The bit offset of the start of the
//                                         range.
//  UINT32 nLength                    IN   The number of bits to clear.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBuffer::clearBits(UINT32 nStart,
                                                       UINT32 nLength,
                                                       Error& err)
{
    // The range extends beyond the end of the page (should never happen).
    if (nStart + nLength > bitSize_)
    {
        err = error_SR_Page_overrun;
        return;
    }

    err = error_None;
    BYTE* pBuffer = (BYTE_PTR)bufferArray_;

    // Mark the affected blocks as "dirty".
    markDirty(pagePosition(nStart), pagePosition(nStart + nLength - 1));
    UINT32 nCurrByte = SpaceBitmapMap::byteComponent(nStart);
    UINT32 nCurrBit = SpaceBitmapMap::bitComponent(nStart);
    UINT32 nLastByte = SpaceBitmapMap::byteComponent(nStart + nLength);
    UINT32 nLastBit = SpaceBitmapMap::bitComponent(nStart + nLength);

    // Clear the bits up to the first byte boundary.
    if (nCurrBit)
    {
        pBuffer[nCurrByte++] &= ~SpaceBitmapMap::maskBits(nCurrBit, (nCurrByte == nLastByte) ? nLastBit : 8);

        // Return if we clear a small number of bits and never cross a byte
        // boundary.
        if (nCurrByte > nLastByte)
            return;
    }

    // Clear the bytes up to the last byte boundary in the range.
    while (nCurrByte < nLastByte)
        pBuffer[nCurrByte++] = 0x00;

    // Clear any residual bits past the last byte boundary.
    if (nLastBit)
        pBuffer[nCurrByte] &= ~SpaceBitmapMap::maskBits(0, nLastBit);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::setBits()               -GKG
//
// Set a range of bits in the buffer.
//
// ARGS:
//  UINT32 nStart                     IN   The bit offset of the start of the
//                                         range.
//  UINT32 nLength                    IN   The number of bits to clear.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::BitmapBuffer::setBits(UINT32 nStart,
                                                UINT32 nLength,
                                                Error& err)
{
    // The range extends beyond the end of the page (should never happen).
    if (nStart + nLength > bitSize_)
    {
        err = error_SR_Page_overrun;
        return;
    }

    err = error_None;
    BYTE* pBuffer = (BYTE_PTR)bufferArray_;

    // Mark the affected blocks as "dirty".
    markDirty(pagePosition(nStart), pagePosition(nStart + nLength - 1));
    UINT32 nCurrByte = SpaceBitmapMap::byteComponent(nStart);
    UINT32 nCurrBit = SpaceBitmapMap::bitComponent(nStart);
    UINT32 nLastByte = SpaceBitmapMap::byteComponent(nStart + nLength);
    UINT32 nLastBit = SpaceBitmapMap::bitComponent(nStart + nLength);

    // Set the bits up to the first byte boundary.
    if (nCurrBit)
    {
        pBuffer[nCurrByte++] |=
            SpaceBitmapMap::maskBits(nCurrBit,(nCurrByte == nLastByte) ? nLastBit : 8);

        // Return if we set a small number of bits and never cross a byte
        // boundary.
        if (nCurrByte > nLastByte)
            return;
    }

    // Set the bytes up to the last byte boundary in the range.
    while (nCurrByte < nLastByte)
        pBuffer[nCurrByte++] = 0xff;

    // Set any residual bits past the last byte boundary.
    if (nLastBit)
        pBuffer[nCurrByte] |= SpaceBitmapMap::maskBits(0, nLastBit);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::findSetRunLength()      -GKG
//
// Find the number of contiguous set bits in the buffer upto a specified
// maximum.  NOTE: If the maximum is zero, continue until the first cleared bit
// or to the end of the buffer.
//
// ARGS:
//  UINT32 nPosition                  IN   The bit offset of the start of the
//                                         range.
//  UINT32 nMaxLength                 IN   The maximum number of set bits to
//                                         scan.
//
// RETURNS:
//  UINT32                                 The number of contiguous set bits.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::UINT32
NSR::BitmapSpaceResource::BitmapBuffer::findSetRunLength(UINT32 nPosition,
                                                                UINT32 nMaxLength) const
{
    // We must start inside the buffer.
    if (nPosition >= bitSize_)
        return 0;

    const BYTE* pBuffer = (CONST_BYTE_PTR)bufferArray_;

    BYTE nByte;
    UINT32 nLength = 0;
    UINT32 nBoundPos = (nMaxLength == 0 || nPosition + nMaxLength > bitSize_) ? bitSize_ : nPosition + nMaxLength;
    UINT32 nCurrBit = SpaceBitmapMap::bitComponent(nPosition);
    UINT32 nCurrByte = SpaceBitmapMap::byteComponent(nPosition);
    UINT32 nLastBit = SpaceBitmapMap::bitComponent(nBoundPos);
    UINT32 nLastByte = SpaceBitmapMap::byteComponent(nBoundPos);

    // Scan the set bits up to the first byte boundary.
    if (nCurrBit)
    {
        UINT32 nEndBit = (nCurrByte == nLastByte && nLastBit) ? nLastBit : 8;
        nByte = pBuffer[nCurrByte++];
        while (nCurrBit < nEndBit && (nByte & SpaceBitmapMap::maskBit(nCurrBit)))
        {
            nCurrBit++;
            nLength++;
        }

        // Return if we found a cleared bit before the byte boundary.
        if (nCurrBit < 8)
            return nLength;
    }

    // Scan completely set bytes up to the last byte boundary.
    while (nCurrByte < nLastByte && pBuffer[nCurrByte] == 0xff)
    {
        nCurrByte++;
        nLength += 8;
    }

    if (nCurrByte < nLastByte)
        nLastBit = 7;

    // Scan any residual set bits past the last byte boundary.
    nCurrBit = 0;
    nByte = pBuffer[nCurrByte];
    while (nCurrBit < nLastBit && (nByte & SpaceBitmapMap::maskBit(nCurrBit++)))
        nLength++;

    return nLength;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::findClearRunLength()    -GKG
//
// Find the number of contiguous clear bits in the buffer upto a specified
// maximum.  NOTE: If the maximum is zero, continue until the first set bit
// or to the end of the buffer.
//
// ARGS:
//  UINT32 nPosition                  IN   The bit offset of the start of the
//                                         range.
//  UINT32 nMaxLength                 IN   The maximum number of clear bits to
//                                         scan.
//
// RETURNS:
//  UINT32                                 The number of contiguous clear bits.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::UINT32
NSR::BitmapSpaceResource::BitmapBuffer::findClearRunLength(UINT32 nPosition,
                                                           UINT32 nMaxLength) const
{
    // We must start inside the buffer.
    if (nPosition >= bitSize_)
        return 0;

    const BYTE* pBuffer = (CONST_BYTE_PTR)bufferArray_;

    BYTE nByte;
    UINT32 nLength = 0;
    UINT32 nBoundPos = (nMaxLength == 0 || nPosition + nMaxLength > bitSize_) ? bitSize_ : nPosition + nMaxLength;
    UINT32 nCurrBit = SpaceBitmapMap::bitComponent(nPosition);
    UINT32 nCurrByte = SpaceBitmapMap::byteComponent(nPosition);
    UINT32 nLastBit = SpaceBitmapMap::bitComponent(nBoundPos);
    UINT32 nLastByte = SpaceBitmapMap::byteComponent(nBoundPos);

    // Scan the clear bits up to the first byte boundary.
    if (nCurrBit)
    {
        UINT32 nEndBit = (nCurrByte == nLastByte && nLastBit) ? nLastBit : 8;
        nByte = pBuffer[nCurrByte++];
        while (nCurrBit < nEndBit && !(nByte & SpaceBitmapMap::maskBit(nCurrBit)))
        {
            nCurrBit++;
            nLength++;
        }

        // Return if we found a set bit before the byte boundary.
        if (nCurrBit < 8)
            return nLength;
    }

    while (nCurrByte < nLastByte && pBuffer[nCurrByte] == 0x00)
    {
        nCurrByte++;
        nLength += 8;
    }

    if (nCurrByte < nLastByte)
        nLastBit = 7;

    // Scan any residual clear bits past the last byte boundary.
    nCurrBit = 0;
    nByte = pBuffer[nCurrByte];
    while (nCurrBit < nLastBit && !(nByte & SpaceBitmapMap::maskBit(nCurrBit++)))
        nLength++;

    return nLength;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBuffer::findSetRunReverse()     -GKG
//
// Find the number of contiguous set bits beginning at a specified bit offset
// an continuing in reverse order to the first cleared bit or the beginning of
// the page data.
//
// ARGS:
//  UINT32 nPosition                  IN   The bit offset of the start of the
//                                         range.
//
// RETURNS:
//  UINT32                                 The number of contiguous set bits.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::UINT32
NSR::BitmapSpaceResource::BitmapBuffer::findSetRunReverse(UINT32 nPosition) const
{
    // We must start inside the buffer.
    if (nPosition >= bitSize_)
        return 0;

    // Deterine the bounding conditions.
    UINT32 nBoundPos = bmPage_->startByteOffset();
    UINT32 nCurrByte = SpaceBitmapMap::byteComponent(nPosition);
    if (nCurrByte < nBoundPos)
        return 0;

    const BYTE* pBuffer = (CONST_BYTE_PTR)bufferArray_;

    BYTE nByte;
    UINT32 nLength = 0;
    UINT32 nCurrBit = SpaceBitmapMap::bitComponent(nPosition);

    // Scan set bits down until the first byte boundary is encountered.
    if (nCurrBit < 7)
    {
        nByte = pBuffer[nCurrByte];
        while (nByte & (1 << nCurrBit))
        {
            nLength++;
            if (nCurrBit == 0)
                break;
            nCurrBit--;
        }

        // If we hit the beginning of data or a clear bit, then return.
        if (nCurrBit > 0 || nCurrByte-- == nBoundPos || nLength == 0 || !(nByte & 0x01))
            return nLength;
    }

    // Scan all completely set bytes in reverse order until a clear bit or the
    // beginning of the buffer data is found.
    while (nCurrByte >= nBoundPos && pBuffer[nCurrByte] == 0xff)
    {
        nLength += 8;
        if (nCurrByte-- == nBoundPos)
            return nLength;
    }

    // Scan any residual set bits below the last byte boundary.
    nCurrBit = 7;
    nByte = pBuffer[nCurrByte];
    while (nByte & SpaceBitmapMap::maskBit(nCurrBit))
    {
        nLength++;
        if (nCurrBit == 0)
            break;
        nCurrBit--;
    }

    return nLength;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBufferCache::BitmapBufferCache() -GKG
//
// Construct the data buffers and initialize the buffer cache.
//
// ARGS:
//  UINT32 nPageSize                  IN   The size (in bytes) of the buffers.
//  UINT32 nNumBuffers                IN   The number of buffers to create.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapBufferCache::BitmapBufferCache(UINT32 nPageSize,
                                                               UINT32 nNumBuffers)
{
    err_ = error_None;
    firstBuffer_ = lastBuffer_ = NULL;

    BitmapBuffer* pPrevBuffer = NULL;

    // Allocate the buffers and construct them in a linked list.
    while (nNumBuffers)
    {
        BitmapBuffer* pCurrBuffer = new BitmapBuffer(nPageSize);
        if (!pCurrBuffer)
        {
            err_ = error_No_free_store;
            return;
        }

        // Append the buffer to the linked list.
        if (pPrevBuffer)
            pPrevBuffer->setNext(pCurrBuffer);
        else
            firstBuffer_ = pCurrBuffer;

        pPrevBuffer = pCurrBuffer;
        nNumBuffers--;
    }

    lastBuffer_ = pPrevBuffer;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBufferCache::~BitmapBufferCache() -GKG
//
// Release all allocated data buffers.
//
// ARGS:       none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapBufferCache::~BitmapBufferCache()
{
    BitmapBuffer* pCurrBuffer = firstBuffer_;

    // Traverse the linked list, deleting the buffers as we go.
    while (pCurrBuffer)
    {
        BitmapBuffer* pNextBuffer = pCurrBuffer->getNext();
        delete pCurrBuffer;
        pCurrBuffer = pNextBuffer;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBufferCache::linkBuffer()       -GKG
//
// Link the specified page to the least-recently used buffer and advance the
// buffer to the most-recently used position.
//
// ARGS:       none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBufferCache::linkBuffer(BitmapPage* pPage,
                                                             Error& err)
{
    err = error_None;
    if (!pPage->getPageBuffer())
    {
        // Link the page to the first available buffer if it is not
        //   already linked.
        firstBuffer_->linkBuffer(pPage, err);
        if (err)
            return;
    }

    if (lastBuffer_ != firstBuffer_)
    {
        // The page is linked now, move it to the end of the queue
        //   since it is the most-recently used (unless there is
        //   only one page).
        BitmapBuffer* pBuffer = firstBuffer_;
        lastBuffer_->setNext(pBuffer);
        firstBuffer_ = pBuffer->getNext();
        pBuffer->setNext(NULL);
        lastBuffer_ = pBuffer;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapBufferCache::syncCache()       -GKG
//
// Scan all buffers and write any "dirty" blocks to media.
//
// ARGS:       none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapBufferCache::syncCache(Error& err)
{
    BitmapBuffer* pBuffer = firstBuffer_;
    while (pBuffer)
    {
        pBuffer->syncBuffer(err);
        if (err)
            return;

        pBuffer = pBuffer->getNext();
    }
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::BitmapPage()              -GKG
//
// Initialize state variables.
//
// ARGS:       none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapPage::BitmapPage()
{
    numBlocks_ = 0;
    pageBeginExtent_ = pageEndExtent_ = 0;
    freeBlocks_ = largestExtent_ = 0;
    bitmapSR_ = NULL;
    pageBuf_ = NULL;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::~BitmapPage()             -GKG
//
// Empty.
//
// ARGS:       none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
NSR::BitmapSpaceResource::BitmapPage::~BitmapPage()
{
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::initialize()              -GKG
//
// Post construction initialization routine.
//
// ARGS:
//  BitmapSpaceResource* pSR          IN   Pointer to the containing
//                                         BitmapSpaceResource object.
//  UINT32 nPageNum                   IN   The page number.
//  UINT32 nStart                     IN   The starting block of this page.
//  Boolean nInit                     IN   True to initialize the page, false
//                                         to read it from disk.
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::initialize(BitmapSpaceResource* pSR,
                                                      UINT32 nPageNum,
                                                      Boolean nInit,
                                                      Error& err)
{
    // Setup state variables.
    pageNum_ = nPageNum;
    bitmapSR_ = pSR;
    numBlocks_ = totalBlocks();

    // Do we create an "empty" page algorithmically, or scan an image from media?
    if (nInit)
    {
        // Clear state variables for "empty" page.
        largestExtent_ = pageBeginExtent_ = freeBlocks_ = pageEndExtent_ = 0;

        // Link a buffer.
        linkBuffer(err);
        if (err)
            return;

        // Zero out the buffer.
        pageBuf_->clear(err);
    }
    else
        this->scanPage(err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::scanPage()                -GKG
//
// Scan the page bit data, and set meta-data to match its state.
//
// ARGS:
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void
NSR::BitmapSpaceResource::BitmapPage::scanPage(Error& err)
{
    // Link to a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Find the position of the first bit in this page.
    UINT32 nPosition = bitToBufferPos(0);

    // Determine how many set bits are next to the beginning of the page.
    UINT32 nLength = pageBuf_->findSetRunLength(nPosition);
    largestExtent_ = pageBeginExtent_ = freeBlocks_ = nLength;
    if (nLength)
        locLargestExtent_ = locFirstFree_ = bufferPosToBit(nPosition);

    // Advance position and alternately scan clear and set runs of bits,
    // accumulating applicable data as we go.
    nPosition += nLength;
    do
    {
        // Find the number of contiguous clear bits from the current position.
        UINT32 nClearLength = pageBuf_->findClearRunLength(nPosition);

        // If the run is zero lengh, we must be done.
        if (!nClearLength)
            break;

        // Advance position and find the number of contiguous set bits.
        nPosition += nClearLength;
        freeBlocks_ += nLength = pageBuf_->findSetRunLength(nPosition);

        // Check to see if this set run is the largest encountered.
        if (nLength > largestExtent_)
        {
            largestExtent_ = nLength;
            locLargestExtent_ = bufferPosToBit(nPosition);
            if (freeBlocks_ == nLength)
                locFirstFree_ = locLargestExtent_;
        }

        // Advance position.
        nPosition += nLength;
    } while (nLength);

    // Set the number of set bits adjoining the end of the page.
    pageEndExtent_ = nLength;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::syncPage()                -GKG
//
// Update any dirty blocks to media.
//
// ARGS:
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::syncPage(Error& err)
{
    // If there are no dirty blocks, we don't need to do this.
    if (!this->isDirty())
        return;

    // Synchonize the buffer.
    pageBuf_->syncBuffer(err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::findLargestExtent()       -GKG
//
// Find the largest contiguous run of free blocks in this page.
//
// ARGS:
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::findLargestExtent(Error& err)
{
    // If there are no free blocks, there is no largest extent to find!
    largestExtent_ = 0;
    UINT32 nBlocksLeft = freeBlocks_;
    if (nBlocksLeft == 0)
        return;

    // Link the page to a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Find the buffer offset of the first free block.
    UINT32 nExtLength;
    UINT32 nPosition = bitToBufferPos(locFirstFree_);
    do
    {
        // Find the number of contiguous free blocks.
        nBlocksLeft -= nExtLength = pageBuf_->findSetRunLength(nPosition);

        // If it is greater than the current largest extent, make it the largest
        // extent.
        if (nExtLength > largestExtent_)
        {
            largestExtent_ = nExtLength;
            locLargestExtent_ = bufferPosToBit(nPosition);
        }

        // Advance to the end of the run and determine if there is enough room
        // left for a larger extent to exist.
        nPosition += nExtLength;
        if (largestExtent_ >= nBlocksLeft)
            return;

        // Advance past the intervening allocated blocks.
        nPosition += nExtLength = pageBuf_->findClearRunLength(nPosition);
    } while (nExtLength);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::takeExtentCommon()        -GKG
//
// Allocate an extent of blocks from this page.  NOTE: This code is common to
// all "take" methods.
//
// ARGS:
//  Extent& clExt                     OUT  The resulting extent (relative to
//                                         this page).
//  UINT32 nPosition                  IN   The start block the allocation will
//                                         begin at.
//  UINT32 nBlocks                    IN   The number of contiguous blocks to
//                                         allocate
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::takeExtentCommon(Extent& clExt,
                                                            UINT32 nPosition,
                                                            UINT32 nBlocks,
                                                            Error& err)
{
    // Clear the bits in the buffer.
    pageBuf_->clearBits(nPosition, nBlocks, err);
    if (err)
    {
        err = error_SR_Bitmap_corrupt;
        return;
    }

    // Determine the bit number and adjust the page meta-data.
    UINT32 nBitNumber = bufferPosToBit(nPosition);
    clExt.location = nBitNumber;
    clExt.length = nBlocks;
    freeBlocks_ -= nBlocks;
    if (freeBlocks_)
    {
        // Update the number of free blocks adjoint the beginning of the page.
        if (nBitNumber < pageBeginExtent_)
            pageBeginExtent_ = nBitNumber;

        // Update the number of free blocks adjoint the end of the page.
        if (nBitNumber >= totalBlocks() - pageEndExtent_)
            pageEndExtent_ = totalBlocks() - nBitNumber - nBlocks;

        // Update the position of the first free block.
        if (nBitNumber == locFirstFree_)
            locFirstFree_ += nBlocks + pageBuf_->findClearRunLength(nPosition + nBlocks);

        // Update the position and size of the largest extent.
        if (nBitNumber == locLargestExtent_)
        {
            largestExtent_ -= nBlocks;
            if (freeBlocks_ - largestExtent_ < largestExtent_)
                locLargestExtent_ += nBlocks;
            else
                findLargestExtent(err);
        }
        else
            if (nBitNumber > locLargestExtent_ && nBitNumber < locLargestExtent_ + largestExtent_)
                findLargestExtent(err);
    }
    else
        pageBeginExtent_ = pageEndExtent_ = largestExtent_ = 0;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::takeExtent()              -GKG
//
// Allocate an extent of contiguous blocks from this page.
//
// ARGS:
//  Extent& clExt                     OUT  The resulting extent (relative to
//                                         this page).
//  UINT32 nBlocks                    IN   The number of contiguous blocks to
//                                         allocate
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::takeExtent(Extent& clExt,
                                                      UINT32 nBlocks,
                                                      Error& err)
{
    // Make sure we have enough contiguous blocks to satisfy this request.
    clExt.length = 0;
    if (nBlocks > largestExtent_)
    {
        err = error_SR_Insufficient_space;
        return;
    }

    // Link the page to a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Find the position of the first free block.
    UINT32 nExtLength;
    UINT32 nPosition = bitToBufferPos(locFirstFree_);

    // Scan each successive run of free blocks until we find one big enough.
    do
    {
        // Find the number of contiguous free blocks.
        nExtLength = pageBuf_->findSetRunLength(nPosition, nBlocks);

        // Is the run big enough?
        if (nExtLength == nBlocks)
            break;

        // Advance past the intervening allocated blocks.
        nPosition += nExtLength;
        nPosition += nExtLength = pageBuf_->findClearRunLength(nPosition);
    } while (nExtLength);

    // We didn't find a run large enough, the meta-data lied to us.
    if (nExtLength < nBlocks)
    {
        err = error_SR_Bitmap_corrupt;
        return;
    }

    // Call the common allocation routine now that we've found a large enough
    // run of free blocks.
    takeExtentCommon(clExt, nPosition, nBlocks, err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::takeExtent()              -GKG
//
// Allocate an specified extent from this page.
//
// ARGS:
//  const Extent& clExt               IN   The extent to allocate (relative to
//                                         this page).
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::takeExtent(const Extent& clExt,
                                                      Error& err)
{
    // Link this page to a buffer.
    linkBuffer(err);
    if (err)
        return;

    UINT32 nLocation = clExt.location;
    UINT32 nPosition = bitToBufferPos(nLocation);

    // Determine if there are enough free blocks at the specified extent to
    // satisfy this request.
    UINT32 nExtLength = pageBuf_->findSetRunLength(nPosition, clExt.length);
    if (nExtLength != clExt.length)
    {
        err = error_SR_Bitmap_corrupt;
        return;
    }

    // Call the common allocation routine now that we've determined this extent
    // is available.
    Extent clTempExt;
    takeExtentCommon(clTempExt, nPosition, clExt.length, err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::takeNextExtent()          -GKG
//
// Allocate an extent of contiguous blocks up to, but not necessarily as large
// as, the specified number of blocks.  The allocated extent will begin at the
// first free block in this page.  NOTE: The resulting exent might not
// completely satisfy the number of requested blocks.  This routine is used to
// constuct extent lists, where the length of each list element is
// indeterminant.
//
// ARGS:
//  Extent& clExt                     OUT  The resulting extent (relative to
//                                         this page).
//  UINT32 nBlocks                    IN   The maximum number of contiguous
//                                         blocks to allocate.
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::takeNextExtent(Extent& clExt,
                                                          UINT32 nBlocks,
                                                          Error& err)
{
    // Return if this page has no free blocks.
    clExt.length = 0;
    if (freeBlocks_ == 0)
        return;

    // Link this page into a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Find the first free block and determine how many contiguous free blocks
    // are there.
    UINT32 nPosition = bitToBufferPos(locFirstFree_);
    UINT32 nExtLength = (locFirstFree_ == 0) ? pageBeginExtent_ :
        (locFirstFree_ == locLargestExtent_) ? largestExtent_ :
        (locFirstFree_ == totalBlocks() - pageEndExtent_) ?
        pageEndExtent_ : pageBuf_->findSetRunLength(nPosition);

    // Zero contiguous extents implies the location of the first block is wrong.
    if (nExtLength == 0)
    {
        err = error_SR_Bitmap_corrupt;
        return;
    }

    // Trim the length if it is more than we need.
    if (nExtLength > nBlocks)
        nExtLength = nBlocks;

    // Call the common allocation routine now that we've found the extent we
    // want to allocate.
    takeExtentCommon(clExt, nPosition, nExtLength, err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::takeBeginExtent()         -GKG
//
// Allocate an extent of contiguous blocks the beginning of this page, upto a
// specified maximum number of blocks.  NOTE: The start block of the resulting
// extent will always be zero (relative to this page).  The resulting extent may
// only partially satisfy the requested number of blocks.  This routine is used
// to build extents which cross page boundaries.
//
// ARGS:
//  Extent& clExt                     OUT  The resulting extent (relative to
//                                         this page).
//  UINT32 nBlocks                    IN   The number of contiguous blocks to
//                                         allocate
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::takeBeginExtent(Extent& clExt,
                                                           UINT32 nBlocks,
                                                           Error& err)
{
    // Determine how many contiguous free block we have at the beginning of this
    // page.
    clExt.length = 0;
    if (pageBeginExtent_ < nBlocks)
        nBlocks = pageBeginExtent_;

    // Link the page into a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Call the common allocation routine now that we've found the extent we
    // want to allocate.
    takeExtentCommon(clExt, bitToBufferPos(0), nBlocks, err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::takeBeginExtent()         -GKG
//
// Allocate an extent of contiguous blocks the end of this page, upto a
// specified maximum number of blocks.  NOTE: The start block + length of the
// resulting extent = page length in blocks (relative to this page).  The
// resulting extent may only partially satisfy the requested number of blocks.
// This routine is used to build extents which cross page boundaries.
//
// ARGS:
//  Extent& clExt                     OUT  The resulting extent (relative to
//                                         this page).
//  UINT32 nBlocks                    IN   The number of contiguous blocks to
//                                         allocate
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::takeEndExtent(Extent& clExt,
                                                         UINT32 nBlocks,
                                                         Error& err)
{
    // Determine how many contiguous free block we have at the end of this page.
    clExt.length = 0;
    if (pageEndExtent_ < nBlocks)
        nBlocks = pageEndExtent_;

    // Link the page into a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Call the common allocation routine now that we've found the extent we
    // want to allocate.
    takeExtentCommon(clExt, bitToBufferPos(totalBlocks() - nBlocks), nBlocks, err);
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::BitmapPage::putExtent()               -GKG
//
// Dellocate an specified extent to this page.
//
// ARGS:
//  const Extent& clExt               IN   The extent to deallocate (relative to
//                                         this page).
//  Error& err                        OUT  The error code if action fails.
//
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------
void NSR::BitmapSpaceResource::BitmapPage::putExtent(const Extent& clExt,
                                                     Error& err)
{
    // Link this page into a buffer.
    linkBuffer(err);
    if (err)
        return;

    // Determine the bit offset inside the buffer.
    UINT32 nLocation = clExt.location;
    UINT32 nPosition = bitToBufferPos(nLocation);

    // If all the blocks at the specified location are not allocated, then there
    // is a problem.
    UINT32 nExtLength = pageBuf_->findClearRunLength(nPosition, clExt.length);
    if (nExtLength != clExt.length)
    {
        err = error_SR_Cannot_free_space;
        return;
    }

    // Set the bits in the buffer.
    pageBuf_->setBits(nPosition, nExtLength, err);
    if (err)
        return;

    // Update the number of free blocks and adjust meta-data values.
    freeBlocks_ += nExtLength;
    if (freeBlocks_ == totalBlocks())
    {
        locFirstFree_ = locLargestExtent_ = 0;
        largestExtent_ = pageBeginExtent_ = pageEndExtent_ = totalBlocks();
        return;
    }

    // Setup meta-data if the freed blocks are the only free blocks in the page.
    if (freeBlocks_ == nExtLength)
    {
        locFirstFree_ = locLargestExtent_ = clExt.location;
        largestExtent_ = nExtLength;
        if (clExt.location == 0)
            pageBeginExtent_ = nExtLength;
        else if (clExt.location + nExtLength == totalBlocks())
            pageEndExtent_ = nExtLength;

        return;
    }

    // Adjust the location of the first free block.
    if (nLocation < locFirstFree_)
        locFirstFree_ = nLocation;

    // Determine how many contiguous free blocks are adjacent the beginning of
    // the extent we just freed.  Scan the bitmap as a last resort.
    UINT32 nBlocksBefore = (nLocation == pageBeginExtent_) ? pageBeginExtent_ :
        (largestExtent_ && nLocation == locLargestExtent_ + largestExtent_) ? largestExtent_ :
        pageBuf_->findSetRunReverse(nPosition - 1);

    nLocation += nExtLength;
    UINT32 nLocEndExtent = totalBlocks() - pageEndExtent_;

    // Determine how many contiguous free blocks are adjacent the end of the
    // extent we just freed.  Scan the bitmap as a last resort.
    UINT32 nBlocksAfter = (largestExtent_ && nLocation == locLargestExtent_) ? largestExtent_ :
        (nLocation == nLocEndExtent) ? pageEndExtent_ :
        pageBuf_->findSetRunLength(nPosition + nExtLength);


    // Adjust the beginning and ending extent lengths.
    if (clExt.location == pageBeginExtent_)
        pageBeginExtent_ += nExtLength + nBlocksAfter;
    else if (nLocation == nLocEndExtent)
        pageEndExtent_ += nExtLength + nBlocksBefore;

    // Adjust the location an length of the largest extent.
    nExtLength += nBlocksBefore + nBlocksAfter;
    if (largestExtent_ < nExtLength || (largestExtent_ == nExtLength && locLargestExtent_ > clExt.location - nBlocksBefore))
    {
        largestExtent_ = nExtLength;
        locLargestExtent_ = clExt.location - nBlocksBefore;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  MasterSpaceResource::takeLogExtent()                       -GKG
//
// Allocate a contiguous block of space from the master space resource.
//
// ARGS:
//  LogExtent& clLogExt     OUT  The allocated extent.
//  UINT32 nLength          IN   The number of bytes to allocate.
//  Error& err              OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------

void
NSR::MasterSpaceResource::takeLogExtent(LogExtent& clLExt,
                                        UINT32 nLength,
                                        Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = error_;
    if (err)
        return;

    // Make sure we've got a valid extent length.
    if (nLength > partp_->maxExtLen())
    {
        err = error_SR_Bad_extent_length;
        return;
    }


    // Allocate the extent from the erased or unerased space resource, give
    // preference to the erased space resource if possible, since extents
    // allocated from it do not need surface preparation.
    if (erasedSRp_ && nLength <= erasedSRp_->maxExtLen())
        erasedSRp_->takeLogExtent(clLExt, nLength, err);
    else if (unerasedSRp_ && nLength <= unerasedSRp_->maxExtLen())
        unerasedSRp_->takeLogExtent(clLExt, nLength, err);
    else
    {
        // We could not satisfy the request.
        err = error_SR_Insufficient_space;
        return;
    }

    error_ = err;
}

//-----------------------------------------------------------------------------
// METHOD:  MasterSpaceResource::takeLogExtentList()                   -GKG
//
// Allocate a potential disjoint set of extents from the master space resource.
//
// ARGS:
//  LogExtentList& clLEList OUT  The allocated extent list.
//  UINT32 nLength          IN   The number of bytes to allocate.
//  Error& err              OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------

void
NSR::MasterSpaceResource::takeLogExtentList(LogExtentList& clLEList,
                                            UINT64 nLength,
                                            Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = error_;
    if (err)
        return;

    // Test to be sure we can satisfy this request.
    if (nLength > availBytes())
    {
        err = error_SR_Insufficient_space;
        return;
    }

    // Clear the list.
    clLEList.clear();

    // Allocate space from the erased space resource first (if it exists),
    // because these extent to not need suface preparation.
    if (erasedSRp_)
    {
        // Make sure there is space available in the erased space resource.
        UINT64 nAllocBytes = erasedSRp_->availBytes();
        if (nAllocBytes)
        {
            // Figure out how much of the request we can satisfy.
            if (nAllocBytes > nLength)
                nAllocBytes = nLength;

            // Allocate an extent list from the erased space resource.
            erasedSRp_->takeLogExtentList(clLEList, nAllocBytes, err);
            if (err)
            {
                error_ = err;
                return;
            }

            // Determine how much of the request is still unsatisfied.
            nLength -= nAllocBytes;
        }
    }

    // Allocate any remaining space from the unerased space resource.
    if (nLength && unerasedSRp_)
    {
        // A temporary list to append to any already allocated extents.
        LogExtentList clTempLEList(*partp_->logVolumep());

        // Take an extent from the unerased space resource.
        unerasedSRp_->takeLogExtentList(clTempLEList, nLength, err);
        if (err)
        {
            error_ = err;

            // If we got an error, try to deallocate anything we got from the
            // erased space resource.
            if (clLEList.numExtents() != 0)
                erasedSRp_->putLogExtentList(clLEList, err);

            // Restore the original error code.
            err = error_;
            return;
        }

        // Concatinate the erased and unerased extents.
        clLEList.concat(clTempLEList, err);
    }
}

//-----------------------------------------------------------------------------
// METHOD:  MasterSpaceResource::putLogExtent()                        -GKG
//
// Deallocate a single extent into the master space resource.
//
// ARGS:
//  const LogExtent& clLogExt         IN   The extent to deallocate.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------

void
NSR::MasterSpaceResource::putLogExtent(const LogExtent& clLExt,
                                       Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = error_;
    if (err)
        return;

    // If we don't have an unerased space resource, we need to erase Unerased or
    // Recorded extents so they can be deallocated.
    LogExtent clTempExt(clLExt);
    if (spaceType() == srSpace_ErasedOnly && (clTempExt.extentType() == Unerased || clTempExt.extentType() == Recorded))
    {
        clTempExt.erase(err);
        if (err)
            return;
    }

    // Make sure this is a valid extent.
    err = validateExtent(clTempExt);
    if (err)
        return;

    // Free the extent into the most appropriate space resource.
    if (unerasedSRp_ && (!erasedSRp_ || clTempExt.extentType() != Erased))
        unerasedSRp_->putLogExtent(clTempExt, err);
    else if (erasedSRp_)
        erasedSRp_->putLogExtent(clTempExt, err);
    else
        err = error_SR_No_valid_space_resource;

    // Trap non-recoverable errors.
    if (err != error_SR_Cannot_free_space)
        error_ = err;
}

//-----------------------------------------------------------------------------
// METHOD:  BitmapSpaceResource::putLogExtentList()                    -GKG
//
// Deallocate a list of extents into the space resource.
//
// ARGS:
//  const LogExtentList& clLEList     IN   The extent to deallocate.
//  Error& err                        OUT  The error code if action fails.
//
// RETURNS:    none
// PRE-CONDS:  none
// POST-CONDS:
//
//-----------------------------------------------------------------------------

void
NSR::MasterSpaceResource::putLogExtentList(const LogExtentList& clLEList,
                                           Error& err)
{
    // Make sure the space resource isn't in an error state.
    err = error_;
    if (err)
        return;

    // Loop through the extent list, deallocating extents as we go.
    LogExtent* pLExt;
    for (UINT32 nExtent = 0; nExtent < clLEList.numExtents(); nExtent++)
    {
        // Get the next extent in the list.
        const LogExtent* pLExt = clLEList.at(nExtent, err);

        // Deallocate a single extent.
        if (!err)
            putLogExtent(*pLExt, err);

        // Stop deallocating space if we hit an error.
        if (err)
            break;
    }
}

