//-*-c++-*--------------------------------------------------------------
//  bmsrTest.cpp
//   Test for BitmapSpaceResource
//
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1996. 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:
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfAncil/ssTest/bmsrTest.cpp,v $
// $Revision: 1.3 $ $Author: hmgr $
// $State: Exp $    $Locker:  $
// $Date: 96/06/22 00:24:55 $

#define TEST_SR_BITMAP

// Okay, so this is a bad thing to do -- but its just for testing.
#define protected   public
#define private     public

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#if defined WINDOWS_PLATFORM
#include <io.h>
#endif
#include "nsrLib.h"
#include "nsrRSSdv.h"

#if defined WINDOWS_PLATFORM || defined MAC_PLATFORM
#include "SysFunc.h"
#endif

//-----------------------------------------------------------------------------
// typedefs for NSR types
//-----------------------------------------------------------------------------

typedef NSR::Bitmap     Bitmap;

//-----------------------------------------------------------------------------
// NSR version information
//-----------------------------------------------------------------------------

UINT8  NSR::osClass         = 5;       // Unix
UINT8  NSR::osIdentifier    = 0;       // Generic Unix

String NSR::implementID     ("HP NSR File Format - SS");
UINT8  NSR::librVersion     = 0;       // Library Version 0.0
UINT16 NSR::implRegNo       = 0xffff;  // Just for testing
UINT8  NSR::implVersion     = 1;       // What Else??
UINT8  NSR::implRevision    = 0xff;    // What Else??

static CONST_CHAR_PTR_CONST DEFAULT_DEVFILE = "/dev/rdsk/c201d4s0";

//-----------------------------------------------------------------------------
// Some local type definitions
//-----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
//  class ExtentListNode
//
//  This class is a node in doubly-linked list.  It provides a list of free
//  extents so we can find valid space to deallocate using putLogExtent() and
//  putLogExtentList().
// ---------------------------------------------------------------------------

class ExtentListNode: public NSR::ExtentDesc
{
private:

    ExtentListNode* prevNode_;
    ExtentListNode* nextNode_;

public:

    ExtentListNode(UINT32 nStart,
                   UINT32 nLength,
                   ExtentListNode* pPrev = NULL,
                   ExtentListNode* pNext = NULL): 
            NSR::ExtentDesc(0)
    {
        location = nStart;
        length = nLength;
        prevNode_ = pPrev;
        nextNode_ = pNext;
    }
};

// ----------------------------------------------------------------------------
//  class ExtentList
//
//  This class is a doubly-linked list of extents.  It provides a list of free
//  extents so we can find valid space to deallocate using putLogExtent() and
//  putLogExtentList().
// ---------------------------------------------------------------------------

class ExtentList
{
private:

    ExtentListNode* firstNode_;
    ExtentListNode* lastNode_;
    ExtentListNode* currentNode_;
    UINT32 numNodes_;

public:

    // Construct an empty list.
    ExtentList()
    {
        firstNode_ = lastNode_ = currentNode_ = NULL;
        numNodes_ = 0;
    }

    // Deallocate the list.
    ~ExtentList()
    { clear(); }

    // Remove all nodes from the list.
    void clear()
    {
        ExtentListNode* pNode = firstNode_;

        // Loop through the nodes, releasing them as we go.
        while (pNode)
        {
            ExtentListNode* pNext = pNode->nextNode_;
            delete pNode;
            pNode = pNext;
        }

        // Mark the list as empty.
        firstNode_ = lastNode_ = currentNode_ = NULL;
        numNodes_ = 0;
    }

    // The number of nodes in the list.
    UINT32 size()
    { return numNodes_; }

    Boolean append(UINT32 nStart, UINT32 nSize);

    Boolean insert(UINT32 nStart, UINT32 nSize);

    Boolean remove();

    // Get a pointer to the current extent.
    NSR::ExtentDesc* getCurrent()
    { return (NSR::ExtentDesc*)currentNode_; }

    // Get a pointer to the first extent in the list.
    NSR::ExtentDesc* getFirst()
    {
        // Make the first node the current node.
        currentNode_ = firstNode_;
        return (NSR::ExtentDesc*)firstNode_;
    }

    // Get a pointer to the last extent in the list.
    NSR::ExtentDesc* getLast()
    {
        // Make the last node the current node.
        currentNode_ = lastNode_;
        return (NSR::ExtentDesc*)lastNode_;
    }

    // Advance to the next extent in the list.
    NSR::ExtentDesc* getNext()
    {
        // Make the next node the current node.
        if (currentNode_)
            currentNode_ = currentNode_->nextNode_;

        return (NSR::ExtentDesc*)currentNode_;
    }

    // Move to the previous extent in the list.
    NSR::ExtentDesc* getPrev()
    {
        // Make the previous node the current node.
        if (currentNode_)
            currentNode_ = currentNode_->prevNode_;

        return (NSR::ExtentDesc*)currentNode_;
    }

    // Get a pointer to a particular node.
    NSR::ExtentDesc* getNode(UINT32 nIndex)
    {
        ExtentListNode* pNode = firstNode_;

        // Go through the list sequentially, until we find the one we want.
        for (UINT32 nCount = 0; nCount < nIndex && pNode; nCount++)
            pNode = pNode->nextNode_;

        // Make this our current node.
        currentNode_ = pNode;

        return (NSR::ExtentDesc*)currentNode_;
    }
};

// Append a new extent to the end of the list.
Boolean ExtentList::append(UINT32 nStart, UINT32 nSize)
{
    // Create the new node.
    ExtentListNode* pNode = new ExtentListNode(nStart, nSize);
    if (pNode == NULL)
        return NSR::False;

    if (firstNode_ == NULL)  // Are we appending to an empty list?
        firstNode_ = lastNode_ = currentNode_ = pNode;
    else
    {
        // Update the pointers of the last node.
        lastNode_->nextNode_ = pNode;
        pNode->prevNode_ = lastNode_;
        pNode->nextNode_ = NULL;
        lastNode_ = pNode;
    }

    // Increment the number of nodes in the list.
    numNodes_++;
    return NSR::True;
}

// Insert a new extent into the list (before the current node).
Boolean ExtentList::insert(UINT32 nStart, UINT32 nSize)
{
    // Create the new node.
    ExtentListNode* pNode = new ExtentListNode(nStart, nSize);
    if (pNode == NULL)
        return NSR::False;

    if (firstNode_ == NULL)  // Are we inserting into an empty list?
        firstNode_ = lastNode_ = currentNode_ = pNode;
    else
    {
        // Use the last node if there is no current node.
        if (currentNode_ == NULL)
            currentNode_ = lastNode_;

        // Update the first node if this will be the head of the list.
        if (currentNode_ == firstNode_)
            firstNode_ = pNode;

        pNode->nextNode_ = currentNode_;

        if (currentNode_ == NULL)
        {
            // Update the pointers to integrate the new node.
            pNode->prevNode_ = lastNode_;
            lastNode_->nextNode_ = pNode;
        }
        else
        {
            // Update the pointers to integrate the new node.
            pNode->prevNode_ = currentNode_->prevNode_;
            if (currentNode_->prevNode_)
                currentNode_->prevNode_->nextNode_ = pNode;
            currentNode_->prevNode_ = pNode;
        }
    }

    // Increment the number of nodes in the list.
    numNodes_++;
    return NSR::True;
}

// Remove the current extent from the list.
Boolean ExtentList::remove()
{
    // Fail if there is no current node.
    if (currentNode_ == NULL)
        return NSR::False;

    // If the current node is the first, we must update the first node.
    if (currentNode_ == firstNode_)
        firstNode_ = currentNode_->nextNode_;

    // If the current node is the last, we must update the last node.
    if (currentNode_ == lastNode_)
        lastNode_ = currentNode_->prevNode_;

    // Update the pointers to exclude the current node from the list.
    if (currentNode_->prevNode_)
        currentNode_->prevNode_->nextNode_ = currentNode_->nextNode_;

    if (currentNode_->nextNode_)
        currentNode_->nextNode_->prevNode_ = currentNode_->prevNode_;

    // Advance the current node.
    ExtentListNode* pNext = currentNode_->nextNode_;

    // Release the node.
    delete currentNode_;

    currentNode_ = pNext;

    // Decrement the number of nodes in the list.
    numNodes_--;

    return NSR::True;
}

// ----------------------------------------------------------------------------
//  class BitmapImageFile
//
//  This class encapsulates the image file to which bitmap reads and writes
//  are redirected.  The file contains the complete image of the
//  SpaceBitmapResource disk image, output is redirected from physical media
//  to this file when the library is compiled with the TEST_SR_BITMAP
//  preprocessor flag defined.
// ---------------------------------------------------------------------------

class BitmapImageFile
{
private:

    FILE* fileHandle_;
    UINT32 fileSize_;
    UINT32 numBits_;
    UINT32 offset_;
    NSR::Partition* partition_;
    NSR::String fileName_;

public:

    BitmapImageFile(CONST_CHAR_PTR pFileName, UINT32 nSize = 0, NSR::Partition* pPart = NULL);

    // Close the file.
    ~BitmapImageFile()
    {
        if (fileHandle_)
            fclose(fileHandle_);
    }

    void Initialize(UINT32 nSize, NSR::Partition* pPart);

    // Returns the logical extent containing the bitmap image.
    void LogExtent(NSR::LogExtent& clLE)
    {
        Error err;
        clLE.reinit(partition_, 0, (partition_ && fileHandle_) ? fileSize_ : 0, err);
    }

    UINT32 Read(BYTE_PTR, UINT32 nSize);

    UINT32 Write(CONST_BYTE_PTR, UINT32 nSize);

    // Change the current file position.
    UINT32 Seek(UINT32 nOffset, UINT32 nMode = 0)
    {
        if (!fileHandle_)
            return 0;
        return (fseek(fileHandle_, nOffset, nMode));
    }

    // Get the current file position.
    UINT32 Position()
    {
        if (!fileHandle_)
            return 0;
        return (ftell(fileHandle_));
    }

    // The offset from the start of the partition to the start of the logical
    // volume.
    UINT32 Offset()
    { return offset_; }

    Boolean SetBitmapBits(UINT32 nPosition, UINT32 nLength);

    // Is the file open?
    Boolean IsValid()
    { return Boolean(fileHandle_ != NULL); }
};

// Create a new image file.
BitmapImageFile::BitmapImageFile(CONST_CHAR_PTR pFileName, UINT32 nBitmapSize, NSR::Partition* pPart)
{
    fileName_ = pFileName;

    if (nBitmapSize && pPart)  // Open the file and initialize the bitmap data if its size was defined.
        Initialize(nBitmapSize, pPart);
    else
    {
        // If no bitmap size is defined, set the file to an invalid state.
        fileHandle_ = NULL;
        fileSize_ = 0;
        offset_ = 0;
        partition_ = NULL;
    }
}

// Open the file and initialize the bitmap data.
void BitmapImageFile::Initialize(UINT32 nBitmapSize, NSR::Partition* pPart)
{
    // If the file is already open, close it.
    if (fileHandle_)
    {
        fclose(fileHandle_);
        fileHandle_ = NULL;
    }

    // If no bitmap size is specified, set the file to an invalid state.
    if (!nBitmapSize || !pPart)
    {
        partition_ = NULL;
        fileSize_ = 0;
        return;
    }

    partition_ = pPart;

    // Open the file.
    fileHandle_ = fopen(fileName_, "w+b");

    if (fileHandle_)
    {
        NSR::LogExtent clLE(partition_, 0, 1);
        LogExtent(clLE);
        NSR::PhysExtent clPE(clLE);

        // Find the offset to the start of the logical volume.
        offset_ = (clPE.startSector() * clPE.sectorSize()) - (clLE.startBlock() * clLE.blockSize());
        NSR::ByteArray clBuffer(4096);
        clBuffer.clear();

        // Setup the descriptor for this bitmap.
        NSR::SpaceBitmapDesc clBMDesc(nBitmapSize);
        numBits_ = clBMDesc.numBits();

        // Determine how big the file needs to be.
        UINT32 nBytesLeft = fileSize_ = (clBMDesc.numBytes() + 24 + offset_ + partition_->blockSize() - 1) / partition_->blockSize() * partition_->blockSize();

        // Initialize the file to all zeroes.
        while (nBytesLeft)
        {
            UINT32 nBytesToWrite = (nBytesLeft > 4096) ? 4096 : nBytesLeft;
            if (fwrite((CONST_BYTE_PTR)clBuffer, 1, nBytesToWrite, fileHandle_) != nBytesToWrite)
            {
                fclose(fileHandle_);
                fileHandle_ = NULL;
                fileSize_ = 0;
                return;
            }

            nBytesLeft -= nBytesToWrite;
        }

        fflush(fileHandle_);

        // Seek to the beginning of the bitmap image an write the header image.
        fseek(fileHandle_, offset_, 0);
        clBMDesc.toISO(clBuffer, 0);
        if (fwrite((CONST_BYTE_PTR)clBuffer, 1, 24, fileHandle_) != 24)
        {
            fclose(fileHandle_);
            fileHandle_ = NULL;
            fileSize_ = 0;
            partition_ = NULL;
            return;
        }
    }
}

// Read a specified number of bytes beginning at the current file poistion.
UINT32 BitmapImageFile::Read(BYTE_PTR pBuffer, UINT32 nSize)
{
    // Make sure the file is valid.
    if (!fileHandle_)
        return 0;

    const UINT32 nChunkSize = 32768L;
    UINT32 nBytesRead = 0;

    // Read the data from the file, in a maximum of 32K chunks.
    while (nSize)
    {
        UINT32 nChunk = (nSize > nChunkSize) ? nChunkSize : nSize;

        // Read this chunk.
        UINT32 nThisRead = fread(pBuffer, 1, nChunk, fileHandle_);
        nBytesRead += nThisRead;

        // If we have a short count, we hit the end of the file.
        if (nThisRead != nChunk)
            break;

        nSize -= nThisRead;
        pBuffer += nThisRead;
    }

    return (nBytesRead);
}

// Write a specified number of bytes beginning at the current file poistion.
UINT32 BitmapImageFile::Write(CONST_BYTE_PTR pBuffer, UINT32 nSize)
{
    // Make sure the file is valid.
    if (!fileHandle_)
        return 0;

    // Constrain the write to the file size (do not grow the file).
    if (Position() + nSize > fileSize_)
        nSize = fileSize_ - Position();

    const UINT32 nChunkSize = 32768L;
    UINT32 nBytesWritten = 0;

    // Write the data to the file, in a maximum of 32K chunks.
    while (nSize)
    {
        UINT32 nChunk = (nSize > nChunkSize) ? nChunkSize : nSize;

        // Write this chunk.
        UINT32 nThisWrite = fwrite(pBuffer, 1, nChunk, fileHandle_);
        nBytesWritten += nThisWrite;

        // If we have a short count, then some kind of disk error occured.
        if (nThisWrite != nChunk)
            break;

        nSize -= nThisWrite;
        pBuffer += nThisWrite;
    }

    return (nBytesWritten);
}

// Set specified bits in the bitmap image.
Boolean BitmapImageFile::SetBitmapBits(UINT32 nPosition, UINT32 nLength)
{
    // Make sure the file and parameters a valid.
    if (!fileHandle_ || nPosition > numBits_ || nPosition + nLength > numBits_)
        return NSR::False;

    // Find the bit offset from the beginning of the file.
    nPosition += (offset_ + 24) << 3;
    UINT32 nByte = nPosition >> 3;
    UINT32 nEndByte = (nPosition + nLength + 7) >> 3;
    UINT32 nBufSize = (nEndByte - nByte);

    // Create a buffer to hold the image data.
    NSR::ByteArray clBA(nBufSize);

    // Seek to the specified position an read the image data.
    if (Seek(nByte) != 0 || Read(clBA, nBufSize) != nBufSize)
        return NSR::False;

    UINT32 nBufPos = 0;
    UINT32 nBit = nPosition & 0x07;
    // Set the bits up to the first byte boundary.
    if (nBit)
    {
        while (nLength && nBit < 8)
        {
            ((BYTE_PTR)clBA)[nBufPos] |= (1 << nBit++);
            nLength--;
        }

        nBufPos++;
    }

    // Set all bits in the bytes upto the last byte boundary of the run.
    while (nLength > 7)
    {
        ((BYTE_PTR)clBA)[nBufPos] = 0xff;
        nLength -= 8;
        nBufPos++;
    }

    // Set the residual bits in the last byte.
    if (nLength > 0)
    {
        nBit = 0;
        while (nLength)
        {
            ((BYTE_PTR)clBA)[nBufPos] |= (1 << nBit++);
            nLength--;
        }
    }

    // Seek to the file position and update the bitmap image.
    if (Seek(nPosition >> 3) != 0 || Write(clBA, nBufSize) != nBufSize)
        return NSR::False;

    return NSR::True;
}

// The static bitmap image file object.
BitmapImageFile clBMFile("bitmap.tmp", 0);

// ----------------------------------------------------------------------------
//  class RandomSRImage
//
//  This class contains BitmapSpaceResource, initializes the state of the
//  bitmap image file and builds the list of allocated space.  The bitmap
//  is initialized based upon three parameters:  A bitmap size, an allocated
//  space parameter, and an unallocated space parameter.  The bitmap size
//  is the size of the BitmapSpaceResource in bits.  The allocated and
//  unallocated space parameters represent the length of the largest possible
//  run of contiguous zero or one bits in the initialized bitmap.  Thus an
//  unallocated space factor of 400 would indicate that the consecutive run
//  of set bits in the bitmap image could be 400.  Thus, the smaller these
//  parameters are, the more fragmented the resulting bitmap is.  The allocated
//  space list simply contains a list of extents corresponding to the runs
//  of zeroes in the bitmap.  This is provided so we can deallocate extents
//  using putLogExtent() and putLogExtentList() that are known to be marked as
//  allocated.  This class encapsulates the test algorithms for takeLogExtent(),
//  takeLogExtentList(), putLogExtent(), and putLogExtentList().  This class
//  also a mechanism for comparing the internal meta-data values of the space
//  resource against the data contained in the bitmap image file.
// ---------------------------------------------------------------------------

class RandomSRBitmap
{
    NSR::BitmapSpaceResource* bitmapSR_;
    ExtentList* allocatedList_;
    UINT32 numBits_;
    UINT32 allocFactor_;
    UINT32 unallocFactor_;
    Boolean isValid_;
    static UINT32 pageSize_;

    void GetBitmapSize(UINT32 nTest);

    void GetAllocatedSize(UINT32 nTest);

    void GetUnallocatedSize(UINT32 nTest);

    void getRandomExtent(NSR::LogExtent& clLE, Boolean nRemove, Boolean nValid, Error& err);

public:

    // Crate a random bitmap from the hard-coded parameter lists.
    RandomSRBitmap(NSR::SingleSurfaceVolume& clVolume, NSR::SRSpaceType nSRType, UINT32 nSizeParam, UINT32 nAllocationParam)
    {
        // Set up the paratmeters for this random bitmap.
        GetBitmapSize(nSizeParam);
        GetAllocatedSize(nAllocationParam);
        GetUnallocatedSize(nAllocationParam);

        // Initialize the random bitmap.
        Initialize(clVolume, nSRType);
    }

    // Create a random bitmap from user-specified parameters.
    RandomSRBitmap(NSR::SingleSurfaceVolume& clVolume, NSR::SRSpaceType nSRType, UINT32 nNumBits, UINT32 nAllocated, UINT32 nUnallocated)
    {
        // Set up the parameters for this random bitmap.
        numBits_ = nNumBits;
        allocFactor_ = nAllocated;
        unallocFactor_ = nUnallocated;

        // Initialize the random bitmap.
        Initialize(clVolume, nSRType);
    }

    ~RandomSRBitmap();

    void Initialize(NSR::SingleSurfaceVolume& clVolume, NSR::SRSpaceType nSRType);

    Boolean BitmapIntegrityCheck();

    // Get a reference to the BitmapSpaceResource.
    NSR::BitmapSpaceResource& bitmapSR()
    { return *bitmapSR_; }

    // Get a reference to the allocated space list.
    ExtentList& allocatedList()
    { return *allocatedList_; }

    // Is the random bitmap valid?
    Boolean isValid()
    { return isValid_; }

    // The number of hard-coded bitmap size parameters.
    static UINT32 numBitmapSizes()
    { return 7; }

    // The number of hard-coded allocation parameters.
    static UINT32 numAllocationFactors()
    { return 7; }

    void getRandomExtent(NSR::LogExtent& clLE, Boolean nValid, Error& err);

    void getRandomExtentList(NSR::LogExtentList& clLEList, Boolean nValid, Error& err);

    Boolean testTakeLogExtent();

    Boolean testTakeLogExtentList();

    Boolean testPutLogExtent();

    Boolean testPutLogExtentList();
};

UINT32 RandomSRBitmap::pageSize_ = 0;

void RandomSRBitmap::Initialize(NSR::SingleSurfaceVolume& clVolume, NSR::SRSpaceType nSRType)
{
    bitmapSR_ = NULL;
    allocatedList_ = NULL;
    isValid_ = NSR::False;

    // Initialize the file image.
    clBMFile.Initialize(numBits_, clVolume.partitionp());
    if (!clBMFile.IsValid())
    {
        printf("Bitmap image file is not valid.\n");
        return;
    }

    // Create a list of allocated extents.
    allocatedList_ = new ExtentList;
    if (allocatedList_ == NULL)
    {
        printf("Could not create allocated space list.\n");
        return;
    }

    // Build the list of allocated extents and set the bits for the initial free
    // space in the bitmap image.
    UINT32 nUnallocatedSpace = 0;
    if (allocFactor_ == 0 || unallocFactor_ == 0)
    {
        // At least on of the allocation parameters is zero (special case).
        if (unallocFactor_ == 0)
        {
            // If the unallocated factor is zero, then the whole volume is allocated.
            if (!allocatedList_->append(0, numBits_))
            {
                printf("Could not append to allocated space list.\n");
                return;
            }
        }
        else
        {
            // If the allocated factor is zero, then the whole volume is unallocated.

            // Set all the bits in the bitmap image.
            clBMFile.SetBitmapBits(0, numBits_);
            nUnallocatedSpace = numBits_;
        }
    }
    else
    {
        // Neither of the allocation parameters are zero, so the bitmap will contain
        // both allocated and unallocated space.

        // Advance through the file alternately setting runs of bits for the free
        // space and appending allocated extents to the allocated list.
        for (UINT32 nCurrentBit = 0; nCurrentBit < numBits_; )
        {
            UINT32 nExtLength;

            // Fifty-percent chance of starting with an allocated extent.
            if (nCurrentBit > 0 || lrand48() & 0x01)
            {
                // Append an allocated extent to the list.

                // Chose a random extent length upto a maximum size of the allocated
                // space parameter.
                nExtLength = (lrand48() % allocFactor_) + 1;

                // Constrain the extent to fit inside the bitmap.
                if (nCurrentBit + nExtLength > numBits_)
                    nExtLength = numBits_ - nCurrentBit;

                // Append the extent to the allocated space list.
                allocatedList_->append(nCurrentBit, nExtLength);

                // Update the current bit position.
                nCurrentBit += nExtLength;
                if (nCurrentBit == numBits_)
                    break;
            }

            // Chose a random extent length upto a maximum size of the unallocated
            // space parameter.
            nExtLength = (lrand48() % unallocFactor_) + 1;

            // Constrain the extent to fit inside the bitmap.
            if (nCurrentBit + nExtLength > numBits_)
                nExtLength = numBits_ - nCurrentBit;

            // Set the corresponding bits in the bitmap image.
            clBMFile.SetBitmapBits(nCurrentBit, nExtLength);

            // Update the current bit position.
            nCurrentBit += nExtLength;
            nUnallocatedSpace += nExtLength;
        }
    }

    // Create the bitmap space resource object.
    bitmapSR_ = new NSR::BitmapSpaceResource(*clVolume.partitionp(), nSRType);
    if (bitmapSR_ == NULL)
    {
        printf("Could not create Bitmap Space Resource object.\n");
        return;
    }

    // Make sure the bitmap space resource was compiled with TEST_SR_BITMAP
    if (bitmapSR_->error() == NSR::error_SR_Debug_code)
        bitmapSR_->err_ = NSR::error_None;
    else
    {
        printf("Bitmap Space Resource was not compiled with TEST_SR_BITMAP defined.\n");
        return;
    }

    // Make sure the bitmap is not in an error state.
    if (bitmapSR_->error())
    {
        printf("Bitmap Space Resource is in error state.  Error: %s\n", bitmapSR_->error().message());
        return;
    }

    pageSize_ = bitmapSR_->bmMap_->pageSize_;

    // Make sure the space resource matches the size of the image file.
    if (bitmapSR_->totalBlocks() != numBits_)
    {
        printf("Bitmap Space Resource has incorrect size.\n");
        return;
    }

    // Make sure the space resource has the number of free blocks expected.
    if (bitmapSR_->freeBlocks_ != nUnallocatedSpace)
    {
        printf("Bitmap Space Resource initialized incorrect free space value.\n");
        return;
    }

    // Make sure the largest extent is a possible value.
    if (allocFactor_ != 0 && bitmapSR_->largestExtent_ > unallocFactor_)
    {
        printf("Bitmap Space Resource has a larger than expected extent.\n");
        return;
    }

    isValid_ = NSR::True;
}

// Tests the integrity of the space resources data variables agains the bitmap data
// contained in the image file.
Boolean RandomSRBitmap::BitmapIntegrityCheck()
{
    // Make sure the file is valid.
    if (!clBMFile.IsValid())
    {
        printf("Image file is not valid.\n");
        return NSR::False;
    }

    // Make sure the number of bits in the bitmap match.
    if (clBMFile.numBits_ != bitmapSR_->bmMap_->numBits_)
    {
        printf("Bitmap size does not match image file size.\n");
        return NSR::False;
    }

    // Make sure the space resource has a valid partition.
    if (bitmapSR_->partp_ == NULL)
    {
        printf("Bitmap object does not have a valid partition.\n");
        return NSR::False;
    }

    // Make sure the page size is an acceptable value.
    if (bitmapSR_->bmMap_->pageSize_ == 0 || bitmapSR_->bmMap_->pageSize_ % bitmapSR_->partp_->blockSize() != 0)
    {
        printf("Page size is not an event multiple of block size.\n");
        return NSR::False;
    }

    // Make sure the space resource allocated the correct number of pages.
    if (bitmapSR_->bmMap_->numPages_ != (24 + (bitmapSR_->bmMap_->numBits_ >> 3) + bitmapSR_->bmMap_->pageSize_ - 1) / bitmapSR_->bmMap_->pageSize_)
    {
        printf("Incorrect number of pages allocated.\n");
        return NSR::False;
    }

    // Make sure the space resource allocated a cache.
    if (bitmapSR_->bufferCache_ == NULL)
    {
        printf("Bitmap has no buffer cache.\n");
        return NSR::False;
    }

    NSR::Error err;

    // Make sure the bitmap image has been updated on the media so we can test the
    // bitmap data against the state variables.
    bitmapSR_->syncToDisk(err);
    if (err)
    {
        printf("Could not update disk image.\n");
        return NSR::False;
    }

    // Read in the bitmap header.
    NSR::ByteArray clHeader(24);
    clBMFile.Seek(clBMFile.offset_);
    if (clBMFile.Read((BYTE_PTR)clHeader, 24) != 24)
    {
        printf("Could not read bitmap header from image.\n");
        return NSR::False;
    }

    // Extract the bitmap header from the buffer and make sure it hasn't been corrupted.
    NSR::SpaceBitmapDesc clDesc;
    clDesc.fromISO((CONST_BYTE_PTR)clHeader, 0);
    if (clDesc.numBits() != clBMFile.numBits_ || clDesc.numBytes() < (clBMFile.numBits_ + 7) >> 3)
    {
        printf("Bitmap header is corrupt.\n");
        return NSR::False;
    }

    UINT32 nLargestExtent = 0;
    UINT32 nFreeBlocks = 0;
    UINT32 nEndExtLength = 0;
    UINT32 nPageFirstFree = bitmapSR_->bmMap_->numPages_;

    // Loop through the pages and make sure the space resource state variables
    // match the combined totals of the pages.
    for (UINT32 nCount = 0; nCount < bitmapSR_->bmMap_->numPages_; nCount++)
    {
        // Find the first page containing free blocks.
        if (bitmapSR_->bmPages_[nCount].freeBlocks_ && nPageFirstFree == bitmapSR_->bmMap_->numPages_)
            nPageFirstFree = nCount;

        // Combine the end extent of the last page with the beginning extent of the
        // current page and see if the combined extent is the largest.
        nEndExtLength += bitmapSR_->bmPages_[nCount].pageBeginExtent_;
        if (nEndExtLength > nLargestExtent)
            nLargestExtent = nEndExtLength;

        // Test if the largest extent in the current page is the largest extent so far.
        if (bitmapSR_->bmPages_[nCount].largestExtent_ > nLargestExtent)
            nLargestExtent = bitmapSR_->bmPages_[nCount].largestExtent_;

        // Get the ending extent of the current page, unless the entire page is
        // free, in which case the beginning extent and end extent are the same.
        if (!bitmapSR_->bmPages_[nCount].pageFree())
            nEndExtLength = bitmapSR_->bmPages_[nCount].pageEndExtent_;

        UINT32 nFree = bitmapSR_->bmPages_[nCount].freeBlocks_;
        nFreeBlocks += nFree;

        // Check to make sure the state variables of the page are feasable.
        if (bitmapSR_->bmPages_[nCount].pageBeginExtent_ > nFree ||
            bitmapSR_->bmPages_[nCount].pageEndExtent_ > nFree ||
            bitmapSR_->bmPages_[nCount].largestExtent_ > nFree ||
            (!bitmapSR_->bmPages_[nCount].pageFree() && (bitmapSR_->bmPages_[nCount].pageBeginExtent_ + bitmapSR_->bmPages_[nCount].pageEndExtent_ > nFree)) ||
            (nFree > 0 && bitmapSR_->bmPages_[nCount].largestExtent_ == 0) ||
            (bitmapSR_->bmPages_[nCount].pageBeginExtent_ > 0 && bitmapSR_->bmPages_[nCount].locFirstFree_ != 0))
        {
            printf("Bitmap page %ld state variables are out of sync.\n", nCount);
            return NSR::False;
        }

        // Make sure this page was properly flushed to disk.
        if (bitmapSR_->bmPages_[nCount].isDirty())
        {
            printf("Bitmap page %ld was not flushed to disk.\n", nCount);
            return NSR::False;
        }
    }

    // Set up a buffer to contain a page of bitmap data.
    NSR::ByteArray clBuffer(bitmapSR_->bmMap_->pageSize_);

    nPageFirstFree = bitmapSR_->bmMap_->numPages_;
    nLargestExtent = 0;
    nFreeBlocks = 0;

    UINT32 nLargestExtentLoc;
    UINT32 nContExtent = 0;
    UINT32 nContExtentLoc = 0;
    UINT32 nBitNumber = 24 << 3;
    UINT32 nEndBitmap = nBitNumber + bitmapSR_->bmMap_->numBits_;
    UINT32 nPage = 0;
    UINT32 nEndPage = 0;

    // Scan the entire bitmap, page by page.  Extract all state pertinent state
    // variable infomation directly from the bitmap data and compare it agains
    // the page and space resource state varibales.
    while (nBitNumber < nEndBitmap)
    {
        UINT32 nRunLoc;
        UINT32 nMaxRunLoc;
        UINT32 nFirstFreeLoc;
        UINT32 nNumFree = 0;
        UINT32 nMaxRun = 0;
        UINT32 nBeginRun = 0;
        UINT32 nEndRun = 0;
        UINT32 nRunLength = 0;

        // Determin the beginning and ending positions of the current page.
        UINT32 nBeginPage = nEndPage;
        nEndPage += (nPage == bitmapSR_->bmMap_->numPages_ - 1) ? ((24 << 3) + bitmapSR_->bmMap_->numBits_) % (bitmapSR_->bmMap_->pageSize_ << 3) : (bitmapSR_->bmMap_->pageSize_ << 3);

        // Seek to the beggining of the current page.
        if (clBMFile.Seek((nBeginPage >> 3) + clBMFile.Offset()) != 0)
        {
            printf("Error seeking in bitmap image file.\n");
            return NSR::False;
        }

        // Read the bitmap data for the current page into the buffer.
        UINT32 nBytesToRead = (nEndPage - nBeginPage + 7) >> 3;
        if (clBMFile.Read((BYTE_PTR)clBuffer, nBytesToRead) != nBytesToRead)
        {
            printf("Error reading from bitmap image file.\n");
            return NSR::False;
        }

        nBeginPage = nBitNumber;

        // Scan the bits of the current page accumulating the pertinent state
        // variable information.
        while (nBitNumber < nEndPage)
        {
            UINT32 nCurrBit = nBitNumber & 0x07;
            UINT32 nCurrByte = (nBitNumber >> 3) % bitmapSR_->bmMap_->pageSize_;

            // Scan through a run of bytes with all bits set.
            if (nCurrBit == 0)
            {
                while (nBitNumber < nEndPage && ((CONST_BYTE_PTR)clBuffer)[nCurrByte] == 0xff)
                {
                    nBitNumber += 8;
                    nRunLength += 8;
                    nCurrByte++;
                }
            }

            // Scan through the set bits in a byte until a clear bit is found.
            BYTE nValue = ((CONST_BYTE_PTR)clBuffer)[nCurrByte];
            while (nCurrBit < 8 && nBitNumber < nEndPage && (nValue & (1 << nCurrBit)))
            {
                nCurrBit++;
                nBitNumber++;
                nRunLength++;
            }

            // Advance to the next byte if all bits are accounted for.
            if (nCurrBit == 8)
            {
                nCurrBit = 0;
                nCurrByte++;
            }

            if (nBitNumber == nEndPage || !(((CONST_BYTE_PTR)clBuffer)[nCurrByte] & (1 << nCurrBit)))
            {
                // We have reached the end of a run of set bits, so accumulate the
                // necessary data.

                // Update the location of the first set bit.
                if (nNumFree == 0)
                    nFirstFreeLoc = nBitNumber - nRunLength - nBeginPage;

                // Adjust the number of set bits found in this page.
                nNumFree += nRunLength;

                // Adjust the position and length of the longest run of set bits found
                // in this page.
                if (nRunLength > nMaxRun)
                {
                    nMaxRun = nRunLength;
                    nMaxRunLoc = nBitNumber - nRunLength - nBeginPage;
                }

                // If the current run is adjacent the beginning of the page data,
                // adjust the beginning run length.
                if (nBitNumber - nRunLength == nBeginPage)
                    nBeginRun = nRunLength;

                // If the current run is adjacent the end of the page data, adjust
                // the end run length.
                if (nBitNumber == nEndPage)
                    nEndRun = nRunLength;

                nRunLength = 0;
            }

            // Scan through a run of bytes with all bits clear.
            if (nCurrBit == 0)
            {
                while (nBitNumber < nEndPage && ((CONST_BYTE_PTR)clBuffer)[nCurrByte] == 0x00)
                {
                    nBitNumber += 8;
                    nCurrByte++;
                }
            }

            // Scan through the set bits in a byte until a set bit is found.
            nValue = ((CONST_BYTE_PTR)clBuffer)[nCurrByte];
            while (nCurrBit < 8 && nBitNumber < nEndPage && !(nValue & (1 << nCurrBit)))
            {
                nCurrBit++;
                nBitNumber++;
            }

            // Advance to the next byte if all bits are accounted for.
            if (nCurrBit == 8)
            {
                nCurrBit = 0;
                nCurrByte++;
            }
        }

        // Accumulate the number of free blocks found.
        nFreeBlocks += nNumFree;
        if (nNumFree && nPageFirstFree == bitmapSR_->bmMap_->numPages_)
            nPageFirstFree = nPage;

        // Adjust the largest extent found to account for extents that contiue
        // across page boundaries.
        if (nContExtent && nBeginRun && nContExtent + nBeginRun > nLargestExtent)
        {
            nLargestExtent = nBeginRun + nContExtent;
            nLargestExtentLoc = nContExtentLoc;
        }

        // Test to see if the largest extent in the current page is the largest
        // extent so far.
        if (nMaxRun > nLargestExtent)
        {
            nLargestExtent = nMaxRun;
            nLargestExtentLoc = nMaxRunLoc + nBeginPage - (24 << 3);
        }

        // Adjust the value of the extent that may be contiued across a page boundary
        // so the cross-boundary extent may be calculated for the next page.
        if (nContExtent && nBeginRun == nEndRun && nBeginRun == bitmapSR_->bmPages_[nPage].totalBlocks())
            nContExtent += nBeginRun;
        else
        {
            nContExtent = nEndRun;
            nContExtentLoc = bitmapSR_->bmPages_[nPage].totalBlocks() - nEndRun + nBeginPage - (24 << 3);
        }

        // Make sure that the number of contiguous set bits at the beginning of the
        // page data matches the state variable in the BitmapPage object.
        if (nBeginRun != bitmapSR_->bmPages_[nPage].pageBeginExtent_)
        {
            printf("Page %ld beginning extent length is incorrect.\n", nPage);
            return NSR::False;
        }

        // Make sure that the number of contiguous set bits at the end of the
        // page data matches the state variable in the BitmapPage object.
        if (nEndRun != bitmapSR_->bmPages_[nPage].pageEndExtent_)
        {
            printf("Page %ld ending extent length is incorrect.\n", nPage);
            return NSR::False;
        }

        // Make sure that the largest number of contiguous set bits in the page
        // data matches the state variable in the BitmapPage object.
        if (nMaxRun != bitmapSR_->bmPages_[nPage].largestExtent_)
        {
            printf("Page %ld largest extent length is incorrect.\n", nPage);
            return NSR::False;
        }

        // Make sure that the location of the largest number of contiguous set bits
        // in the page data matches the state variable in the BitmapPage object.
        if (nMaxRun && nMaxRunLoc != bitmapSR_->bmPages_[nPage].locLargestExtent_)
        {
            printf("Page %ld largest extent location is incorrect.\n", nPage);
            return NSR::False;
        }

        // Make sure the total number of set bits in the page data matches the state
        // variable in the BitmapPage object.
        if (nNumFree != bitmapSR_->bmPages_[nPage].freeBlocks_)
        {
            printf("Page %ld number of free blocks is incorrect.\n", nPage);
            return NSR::False;
        }

        // Make sure the location of the first set bit in the page data matches the
        // state variable in the BitmapPage object.
        if (nNumFree && nFirstFreeLoc != bitmapSR_->bmPages_[nPage].locFirstFree_)
        {
            printf("Page %ld location of first free block is incorrect.\n", nPage);
            return NSR::False;
        }

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

    // Make sure the number of free blocks found by scanning all pages matches the
    // number of free blocks in the BitmapSpaceResource.
    if (nFreeBlocks != bitmapSR_->freeBlocks_)
    {
        printf("Bitmap Space Resource number of free blocks is incorrect.\n");
        return NSR::False;
    }

    // Make sure the largest number of contiguous set bits (including runs which
    // span page boundaries) found by scanning all pages matches largest extent
    // of free blocks in the BitmapSpaceResource.
    if (nLargestExtent != bitmapSR_->largestExtent_)
    {
        printf("Bitmap Space Resource largest extent is incorrect.\n");
        return NSR::False;
    }

    // Make sure the location of the largest number of contigous set bits found
    // by scanning all pages matches the location of the largest extent of free
    // blocks in the BitmapSpaceResource.
    if (nLargestExtent && nLargestExtentLoc != bitmapSR_->locLargestExtent_)
    {
        printf("Bitmap Space Resource location largest extent is incorrect.\n");
        return NSR::False;
    }

    // Test succeeded.
    return NSR::True;
}

// Release heap-allocated objects.
RandomSRBitmap::~RandomSRBitmap()
{
    delete bitmapSR_;
    delete allocatedList_;
}

// Select a hard-coded bitmap size parameter.
void RandomSRBitmap::GetBitmapSize(UINT32 nTest)
{
    switch (nTest)
    {
    case 1:
        numBits_ = (pageSize_) ? pageSize_ - (24 << 3) : 512;
        break;
    case 2:
        numBits_ = (pageSize_) ? pageSize_ : 512;
        break;
    case 3:
        numBits_ = (pageSize_) ? pageSize_ - 123 : 512;
        break;
    case 4:
        numBits_ = (pageSize_) ? 15 * pageSize_ - (24 << 3) : 512;
        break;
    case 5:
        numBits_ = (pageSize_) ? 11 * pageSize_ - (24 << 3) + 3 : 512;
        break;
    case 6:
        numBits_ = (pageSize_) ? 11 * pageSize_ - (24 << 3) - 3 : 512;
        break;
    default:
        numBits_ = 200000L;
    }
}

// Select a hard-coded allocated space parameter.
void RandomSRBitmap::GetAllocatedSize(UINT32 nTest)
{
    switch (nTest)
    {
    case 1:
        allocFactor_ = 0;
        break;
    case 2:
        allocFactor_ = (numBits_ > 500) ? numBits_ / 500 : 512;
        break;
    case 3:
        allocFactor_ = (numBits_ > 1000) ? numBits_ / 1000 : 512;
        break;
    case 4:
        allocFactor_ = (numBits_ > 250) ? numBits_ / 250 : 512;
        break;
    case 5:
        allocFactor_ = 35;
        break;
    case 6:
        allocFactor_ = 3;
        break;
    default:
        allocFactor_ = 1;
    }
}

// Select a hard-coded unallocated space parameter.
void RandomSRBitmap::GetUnallocatedSize(UINT32 nTest)
{
    switch (nTest)
    {
    case 1:
        unallocFactor_ = 1;
        break;
    case 2:
        unallocFactor_ = (numBits_ > 500) ? numBits_ / 500 : 512;
        break;
    case 3:
        unallocFactor_ = (numBits_ > 250) ? numBits_ / 250 : 512;
        break;
    case 4:
        unallocFactor_ = (numBits_ > 1000) ? numBits_ / 1000 : 512;
        break;
    case 5:
        unallocFactor_ = 35;
        break;
    case 6:
        unallocFactor_ = 3;
        break;
    default:
        unallocFactor_ = 0;
    }
}

// Get a random extent for dealocation using putLogExtent or putLogExtentList.
void RandomSRBitmap::getRandomExtent(NSR::LogExtent& clLE, Boolean nRemove, Boolean nValid, Error& err)
{
    // Set the extent to zero.
    clLE.reinit(bitmapSR_->partp_, 0, 0, err);
    if (err || !allocatedList_)
        return;

    // Make sure we have extents in the allocated list.
    UINT32 nSize = allocatedList_->size();
    if (!nSize)
        return;

    // Choose a random extent in the allocated list.
    UINT32 nIndex = lrand48() % nSize;
    NSR::ExtentDesc* pExtent = allocatedList_->getNode(nIndex);
    if (!pExtent)
        return;

    // Extract the location and size of the chosen extent.
    UINT32 nLocation = pExtent->location;
    UINT32 nExtPosition = 0;
    UINT32 nExtLength = pExtent->length;

    if (nValid)
    {
        // We want to chose a valid extent so that the putLogExtent() or
        // putLogExtentList() call will succeed.
        UINT32 nLocation = pExtent->location;

        // Make our selected extent be some random subset of the chose extent.
        nExtPosition = lrand48() % pExtent->length;
        nExtLength = (lrand48() % (pExtent->length - nExtPosition)) + 1;

        if (nRemove)
        {
            // We want to remove the selected extent from the allocated space list
            // because the call to putLogExtent() or putLogExtentList() is supposed
            // to succeed, and thus it will no longer be allocated space when we
            // are done.

            if (nExtPosition)
            {
                // Our selected extent begins somewhere in the middle of the extent
                // we chose from the list.

                // If the selected extent does not end at the end or the extent chosen
                // from the list, we must calculate the residual extent and append it
                // to the end of the list.
                if (nExtPosition + nExtLength < pExtent->length)
                    allocatedList_->append(pExtent->location + nExtPosition + nExtLength, pExtent->length - nExtPosition - nExtLength);

                // Adjust the length of the extent chosen in the list to include only
                // the space up to the first block of the selected extent.
                pExtent->length = nExtPosition;
            }
            else
            {
                // Our selected extent begins at the first block of the extent we
                // chose from the list.

                if (nExtLength == pExtent->length)
                {
                    // Our selected extent is exactly equal to the one chosen from
                    // the allocated list, so we remove it entirely.
                    allocatedList_->remove();
                }
                else
                {
                    // Adjust the location and length of the extent chosen from the list
                    // to include only the space not accounted for by the selected extent.
                    pExtent->location += nExtLength;
                    pExtent->length -= nExtLength;
                }
            }
        }
    }
    else
    {
        // We are going to generate an invalid extent so that the call to putLogExtent()
        // or putLogExtentList() will fail.  This allows us to test the error handling
        // capabilities of the BitmapSpaceResource.

        // Choose one of three possible error conditions.
        switch (lrand48() % 3)
        {
        case 0: // Subtract offset from extent location
            if (nLocation)
            {
                nLocation -= (lrand48() % ((nExtLength > nLocation) ? nLocation : nExtLength)) + 1;
                break;
            }
        case 1: // Add offset to extent location
            if (nLocation < bitmapSR_->totalBlocks())
            {
                nLocation += (lrand48() % nExtLength) + 1;
                break;
            }
        case 2: // Lengthen the extent
            nExtLength = pExtent->length + (lrand48() % pExtent->length) + 1;
        }
    }

    nExtPosition += nLocation;
    NSR::UINT64 nNumBytes = (nExtLength * bitmapSR_->partp_->blockSize());

    // Initialize the extent to reflect the value of the selected extent.
    clLE.reinit(bitmapSR_->partp_, nExtPosition, nNumBytes, err);
    if (err == NSR::error_LE_beyond_partition)
        err = NSR::error_None;

    if (err)
        return;
}

// Get a random extent with default parameters.
void RandomSRBitmap::getRandomExtent(NSR::LogExtent& clLE, Boolean nValid, Error& err)
{
    getRandomExtent(clLE, NSR::True, nValid, err);
}

// Get a list of random extents.
void RandomSRBitmap::getRandomExtentList(NSR::LogExtentList& clLEList, Boolean nValid, Error& err)
{
    // Clear the output list.
    clLEList.clear();

    // Determine if we have allocated extents in the allocated space list.
    UINT32 nSize = allocatedList_->size();
    if (!nSize)
        return;

    // Determine how many extents we want to put into this list.
    UINT32 nExtents = (lrand48() % nSize) + 1;

    // If we are creating an invalid list to test error trapping, select a pariticular
    // extent in the resulting list to be invalid.  This allows us to test bitmap
    // state rollback, as most of the extents in the list will be valid.
    UINT32 nInvalidExtent = (lrand48() % nExtents) + 1;

    // Build the extent list.
    while (nExtents)
    {
        NSR::LogExtent clLE;

        // Get a random extent.
        getRandomExtent(clLE, nValid, (!nValid && nExtents == nInvalidExtent) ? NSR::False : NSR::True, err);
        if (err)
            return;

        // Append the extent to the list.
        clLEList.append(clLE, err);
        if (err)
            return;

        nExtents--;
    }
}

// Test algorithm for BitmapSpaceResource::takeLogExtent()
Boolean RandomSRBitmap::testTakeLogExtent()
{
    printf("\nTesting BitmapSpaceResource::takeLogExtent().\n");
    printf("  Bitmap Size:              %8ld blocks (bits)\n", bitmapSR_->bmMap_->numBits_);
    printf("  Free Space:               %8ld blocks (bits)\n", bitmapSR_->freeBlocks_);
    printf("  Allocated Space Factor:   %8ld\n", allocFactor_);
    printf("  Unallocated Space Factor: %8ld\n", unallocFactor_);


    UINT32 nCount = 0;

    // Do test until there are no free blocks left in the space resource.
    do
    {
        NSR::Error err;
        NSR::LogExtent clLE;

        UINT32 nLargestExtent = bitmapSR_->largestExtent_;
        UINT32 nFreeBlocks = bitmapSR_->freeBlocks_;

        // UINT64 is not fully implemented yet -- so we must trap wraparound conditions.
        NSR::UINT64 nLargestInBytes = ((NSR::UINT64)nLargestExtent * (NSR::UINT64)clBMFile.partition_->blockSize() / (NSR::UINT64)clBMFile.partition_->blockSize() == (NSR::UINT64)nLargestExtent) ?
            (NSR::UINT64)nLargestExtent * (NSR::UINT64)clBMFile.partition_->blockSize() :
            MAX_UINT32;

        if (!(nCount & 0x07) && nLargestInBytes != MAX_UINT32)
        {
            // We will try to allocate an invalid request every eight iteration to
            // test error trapping.

            // Allocate an extent larger than the largest extent.
            NSR::UINT64 nSizeInBytes = nLargestInBytes + (lrand48() % (UINT32)(MAX_UINT32 - nLargestInBytes)) + 1;
            bitmapSR_->takeLogExtent(clLE, nSizeInBytes, err);

            // Make sure we trapped the error.
            if (!err)
            {
                printf("takeLogExtent() did not flag an error when allocating a extent that is too large.\n");
                return NSR::False;
            }

            // Make sure the largest extent state variable was not modified.
            if (bitmapSR_->largestExtent_ != nLargestExtent)
            {
                printf("takeLogExtent() modified largest extent data whan allocating an extent that is too large.\n");
                return NSR::False;
            }

            // Make sure the number of free blocks was not modified.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks)
            {
                printf("takeLogExtent() modified free blocks data when allocating an extent that is too large.\n");
                return NSR::False;
            }
        }
        else
        {
            // Here is where we try allocating valid allocation requests.

            // Allocate an extent that the space resource should be able to satisfy.
            NSR::UINT64 nSizeInBytes = (lrand48() % (UINT32)(nLargestInBytes)) + 1;
            bitmapSR_->takeLogExtent(clLE, nSizeInBytes, err);

            // Make sure we didn't get an error.  We don't care if the extent was
            // beyond the end of partition for testing purposes.
            if (err && err != NSR::error_LE_beyond_partition)
            {
                printf("takeLogExtent() return an error.  Error: %s.\n", err.message());
                return NSR::False;
            }

            // Make sure the extent is the size we requested.
            if (clLE.infoLen() != nSizeInBytes)
            {
                printf("takeLogExtent() returned an extent of the wrong length.\n");
                return NSR::False;
            }

            UINT32 nNumBlocks = clLE.numBlocks();

            // Make sure the number of free blocks was adjusted properly.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks - nNumBlocks)
            {
                printf("takeLogExtent() modified free blocks data incorrectly.\n");
                return NSR::False;
            }

            // Make sure the largest extent is a possible value.
            if (bitmapSR_->largestExtent_ < nLargestExtent - nNumBlocks)
            {
                printf("takeLogExtent() modified largest extent data incorrectly.\n");
                return NSR::False;
            }
        }

        // Do a full check of the entire bitmap image.
        if (!BitmapIntegrityCheck())
            return NSR::False;

        nCount++;
    } while (bitmapSR_->freeBlocks_);

    // Test succeeded.
    return NSR::True;
}

// Test alogorithm for BitmapSpaceResource::takeLogExtentList()
Boolean RandomSRBitmap::testTakeLogExtentList()
{
    printf("\nTesting BitmapSpaceResource::takeLogExtentList().\n");
    printf("  Bitmap Size:              %8ld blocks (bits)\n", bitmapSR_->bmMap_->numBits_);
    printf("  Free Space:               %8ld blocks (bits)\n", bitmapSR_->freeBlocks_);
    printf("  Allocated Space Factor:   %8ld\n", allocFactor_);
    printf("  Unallocated Space Factor: %8ld\n", unallocFactor_);

    UINT32 nCount = 0;

    // Do test until there are no free blocks left in the space resource.
    do
    {
        NSR::Error err;
        NSR::LogExtentList clLEL(*clBMFile.partition_->logVolumep());

        UINT32 nLargestExtent = bitmapSR_->largestExtent_;
        UINT32 nFreeBlocks = bitmapSR_->freeBlocks_;

        // UINT64 is not fully implemented yet -- so we must trap wraparound conditions.
        NSR::UINT64 nLargestInBytes = ((NSR::UINT64)nFreeBlocks * (NSR::UINT64)clBMFile.partition_->blockSize() / (NSR::UINT64)clBMFile.partition_->blockSize() == (NSR::UINT64)nFreeBlocks) ?
            (NSR::UINT64)nFreeBlocks * (NSR::UINT64)clBMFile.partition_->blockSize() :
            MAX_UINT32;

        if (!(nCount & 0x07) && nLargestInBytes != MAX_UINT32)
        {
            // We will try to allocate an invalid request every eight iteration to
            // test error trapping.

            // Allocate an extent list larger than the number of free blocks.
            NSR::UINT64 nSizeInBytes = nLargestInBytes + (lrand48() % (UINT32)(MAX_UINT32 - nLargestInBytes)) + 1;
            bitmapSR_->takeLogExtentList(clLEL, nSizeInBytes, err);

            // Make sure we trapped the error.
            if (!err)
            {
                printf("takeLogExtentList() did not flag an error when allocating a extent that is too large.\n");
                return NSR::False;
            }

            // Make sure the largest extent state variable was not modified.
            if (bitmapSR_->largestExtent_ != nLargestExtent)
            {
                printf("takeLogExtentList() modified largest extent data whan allocating an extent that is too large.\n");
                return NSR::False;
            }

            // Make sure the number of free blocks was not modified.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks)
            {
                printf("takeLogExtentList() modified free blocks data when allocating an extent that is too large.\n");
                return NSR::False;
            }
        }
        else
        {
            // Here is where we try allocating valid allocation requests.

            // Allocate an extent list that the space resource should be able to satisfy.
            NSR::UINT64 nSizeInBytes = (lrand48() % (UINT32)(nLargestInBytes)) + 1;
            bitmapSR_->takeLogExtentList(clLEL, nSizeInBytes, err);

            // Make sure we didn't get an error.  We don't care if the extent was
            // beyond the end of partition for testing purposes.
            if (err && err != NSR::error_LE_beyond_partition)
            {
                printf("takeLogExtentList() return an error.  Error: %s.\n", err.message());
                return NSR::False;
            }

            // Make sure the extent is the size we requested.
            if (clLEL.infoLen() != nSizeInBytes)
            {
                printf("takeLogExtentList() returned an extent of the wrong length.\n");
                return NSR::False;
            }

            UINT32 nNumBlocks = clLEL.blockMultLen() / bitmapSR_->blockSize();

            // Make sure the number of free blocks was adjusted properly.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks - nNumBlocks)
            {
                printf("takeLogExtentList() modified free blocks data incorrectly.\n");
                return NSR::False;
            }

            // Make sure the largest extent is a possible value.
            if (nLargestExtent >= nNumBlocks && bitmapSR_->largestExtent_ < nLargestExtent - nNumBlocks)
            {
                printf("takeLogExtentList() modified largest extent data incorrectly.\n");
                return NSR::False;
            }
        }

        // Do a full check of the entire bitmap image.
        if (!BitmapIntegrityCheck())
            return NSR::False;

        nCount++;
    } while (bitmapSR_->freeBlocks_);

    // Test succeeded.
    return NSR::True;
}

// Test alogorithm for BitmapSpaceResource::putLogExtent()
Boolean RandomSRBitmap::testPutLogExtent()
{
    printf("\nTesting BitmapSpaceResource::putLogExtent().\n");
    printf("  Bitmap Size:              %8ld blocks (bits)\n", bitmapSR_->bmMap_->numBits_);
    printf("  Free Space:               %8ld blocks (bits)\n", bitmapSR_->freeBlocks_);
    printf("  Allocated Space Factor:   %8ld\n", allocFactor_);
    printf("  Unallocated Space Factor: %8ld\n", unallocFactor_);

    UINT32 nCount = 0;

    // Do test until there all block in space resource are free.
    do
    {
        NSR::Error err;
        NSR::LogExtent clLE;

        UINT32 nLargestExtent = bitmapSR_->largestExtent_;
        UINT32 nFreeBlocks = bitmapSR_->freeBlocks_;

        if (!(nCount & 0x07))
        {
            // We will try to deallocate an invalid request every eight iteration to
            // test error trapping.

            // Make sure there are still allocated extents to draw from.
            if (allocatedList_->size() == 0)
                return NSR::True;

            // Get an invalid extent.
            getRandomExtent(clLE, NSR::False, err);
            if (err || clLE.infoLen() == 0)
            {
                printf("getRandomExtent() returned a zero-length extent.\n");
                return NSR::False;
            }

            // Try to deallocate the invalid extent.
            bitmapSR_->putLogExtent(clLE, err);

            // Make sure we trapped the error.
            if (!err)
            {
                printf("putLogExtent() did not flag an error when allocating an invalid extent.\n");
                return NSR::False;
            }

            // Make sure the largest extent state variable was not modified.
            if (bitmapSR_->largestExtent_ != nLargestExtent)
            {
                printf("putLogExtent() modified largest extent data whan deallocating an invalid extent.\n");
                return NSR::False;
            }

            // Make sure the number of free blocks was not modified.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks)
            {
                printf("putLogExtent() modified free blocks data when deallocating an invalid extent.\n");
                return NSR::False;
            }
        }
        else
        {
            // Here is where we try valid deallocation requests.

            // Make sure there are still allocated extents to draw from.
            if (allocatedList_->size() == 0)
                return NSR::True;

            // Get a valid allocated extent.
            getRandomExtent(clLE, NSR::True, err);
            if (err || clLE.infoLen() == 0)
            {
                printf("getRandomExtent() returned a zero-length extent.\n");
                return NSR::False;
            }

            // Deallocate the extent.
            bitmapSR_->putLogExtent(clLE, err);

            // Make sure we didn't get an error.
            if (err)
            {
                printf("putLogExtent() return an error.  Error: %s.\n", err.message());
                return NSR::False;
            }

            UINT32 nNumBlocks = clLE.numBlocks();

            // Make sure the number of free blocks was adjusted properly.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks + nNumBlocks)
            {
                printf("putLogExtent() modified free blocks data incorrectly.\n");
                return NSR::False;
            }
        }

        // Do a full check of the entire bitmap image.
        if (!BitmapIntegrityCheck())
            return NSR::False;

        nCount++;
    } while (bitmapSR_->freeBlocks_ < bitmapSR_->totalBlocks());

    // Test succeeded.
    return NSR::True;
}

// Test alogorithm for BitmapSpaceResource::putLogExtentList()
Boolean RandomSRBitmap::testPutLogExtentList()
{
    printf("\nTesting BitmapSpaceResource::putLogExtentList().\n");
    printf("  Bitmap Size:              %8ld blocks (bits)\n", bitmapSR_->bmMap_->numBits_);
    printf("  Free Space:               %8ld blocks (bits)\n", bitmapSR_->freeBlocks_);
    printf("  Allocated Space Factor:   %8ld\n", allocFactor_);
    printf("  Unallocated Space Factor: %8ld\n", unallocFactor_);

    UINT32 nCount = 0;

    // Do test until there all block in space resource are free.
    do
    {
        NSR::Error err;
        NSR::LogExtentList clLEList(*clBMFile.partition_->logVolumep());

        UINT32 nLargestExtent = bitmapSR_->largestExtent_;
        UINT32 nFreeBlocks = bitmapSR_->freeBlocks_;

        if (!(nCount & 0x07))
        {
            // We will try to deallocate an invalid request every eight iteration to
            // test error trapping.

            // Make sure there are still allocated extents to draw from.
            if (allocatedList_->size() == 0)
                return NSR::True;

            // Get an extent list with an invalid extent.
            getRandomExtentList(clLEList, NSR::False, err);
            if (err || clLEList.infoLen() == 0)
            {
                printf("getRandomExtent() returned a zero-length extent.\n");
                return NSR::False;
            }

            // Try to deallocate the invalid extent list.
            bitmapSR_->putLogExtentList(clLEList, err);

            // Make sure we trapped the error.
            if (!err)
            {
                printf("putLogExtentList() did not flag an error when allocating an invalid extent.\n");
                return NSR::False;
            }

            // Make sure the largest extent state variable was not modified.
            if (bitmapSR_->largestExtent_ != nLargestExtent)
            {
                printf("putLogExtentList() modified largest extent data whan deallocating an invalid extent.\n");
                return NSR::False;
            }

            // Make sure the number of free blocks was not modified.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks)
            {
                printf("putLogExtentList() modified free blocks data when deallocating an invalid extent.\n");
                return NSR::False;
            }
        }
        else
        {
            // Here is where we try valid deallocation requests.

            // Make sure there are still allocated extents to draw from.
            if (allocatedList_->size() == 0)
                return NSR::True;

            // Get a valid allocated extent list.
            getRandomExtentList(clLEList, NSR::True, err);
            if (err || clLEList.infoLen() == 0)
            {
                printf("getRandomExtent() returned a zero-length extent.\n");
                return NSR::False;
            }

            // Deallocate the extent list.
            bitmapSR_->putLogExtentList(clLEList, err);
            if (err)
            {
                printf("putLogExtentList() return an error.  Error: %s.\n", err.message());
                return NSR::False;
            }

            UINT32 nNumBlocks = clLEList.blockMultLen() / bitmapSR_->blockSize();

            // Make sure the number of free blocks was adjusted properly.
            if (bitmapSR_->freeBlocks_ != nFreeBlocks + nNumBlocks)
            {
                printf("putLogExtentList() modified free blocks data incorrectly.\n");
                return NSR::False;
            }
        }

        // Do a full check of the entire bitmap image.
        if (!BitmapIntegrityCheck())
            return NSR::False;

        nCount++;
    } while (bitmapSR_->freeBlocks_ < bitmapSR_->totalBlocks());

    // Test succeeded.
    return NSR::True;
}

//-----------------------------------------------------------------------------
// Global functions
//-----------------------------------------------------------------------------

// Get the logical extent that constains the space bitmap.
void GetBitmapExtent(NSR::LogExtent& clLE)
{
    clBMFile.LogExtent(clLE);
}

// Read bitmap data from the disk image to a memory buffer.
void ReadBitmapImage(const NSR::LogExtent& clLE, ByteArrayRef& clBARef, Error& err)
{
    err = NSR::error_None;

    // Determine the start block and length of the read.
    UINT32 nPosition = clLE.startBlock() * clLE.blockSize() + clBMFile.Offset();
    UINT32 nLength = clLE.blockMultLen();

    // Seek to the start position.
    if (clBMFile.Seek(nPosition) != 0)
    {
        err = NSR::error_SR_Bitmap_read;
        return;
    }

    // Read the data into a memory buffer.
    if (clBMFile.Read(clBARef, nLength) != nLength)
    {
        err = NSR::error_SR_Bitmap_read;
        return;
    }
}

// Write bitmap data from a memory buffer to the disk image.
void WriteBitmapImage(const NSR::LogExtent& clLE, const ByteArrayRef& clBARef, Error& err)
{
    err = NSR::error_None;

    // Determine the start block and length of the read.
    UINT32 nPosition = clLE.startBlock() * clLE.blockSize() + clBMFile.Offset();
    UINT32 nLength = clLE.blockMultLen();

    // Seek to the start position.
    if (clBMFile.Seek(nPosition) != 0)
    {
        err = NSR::error_SR_Bitmap_write;
        return;
    }

    // Write the memory buffer to disk.
    if (clBMFile.Write(clBARef, nLength) != nLength)
    {
        err = NSR::error_SR_Bitmap_write;
        return;
    }
}

//-----------------------------------------------------------------------------
// main():
//
// We expect one argument, the device file to test on; if we don't
// get any arguments, or an argument we don't understand, we just use some
// default ("/dev/rdsk/c201d4s0").  Note that this program performs
// DESTRUCTIVE WRITES on the disk device given as argument!!!!!!
//-----------------------------------------------------------------------------


int main (int argc, CHAR_PTR argv[])
{
    String  *str;
    INT32   rc;
    NSR::Error err;
// Prepare a String variable for use with the DriveDevice constructor.

#if defined WINDOWS_PLATFORM || defined MAC_PLATFORM
    int scsiID;   // scsi ID
    char buffer[256];
    DiskAddr* pDA;

    // Get the desired SCSI ID (-1 = dummy device file).
    scsiID = -1;
    printf("Enter SCSI device ID (default = -1): ");
    fgets(buffer, 256, stdin);
    if (buffer[0] != '\0' && buffer[0] != '\n')
        scsiID = atoi(buffer);

    // Setup the DiskAddr.
    if (scsiID == -1)
        pDA = new DummyDiskAddr;
    else
#if defined MAC_PLATFORM
        pDA = new MacAddr(scsiID, 0, 0);
#elif defined WINDOWS_PLATFORM
    pDA = new SCSIAddr(scsiID, 0, 0);
#elif defined UNIX_PLATFORM
    pDA = new DummyDiskAddr;
#endif

    // Contruct the drive device.
    DriveDevice ddev(pDA, err);
#else
    CONST_CHAR_PTR devFile;   // device file name

    if (argc < 2)
        devFile = "tbox";   // default
    else
        devFile = argv[1];
    NSR::DriveDevice ddev(devFile);
#endif
    if(ddev.error())
    {
        printf("Drive Device construction failed.  Error code: %ld\n", ddev.error());
        exit(1);
    }

    // Print the device paramters.
    printf("\nDriveDevice block size:  %10ld Bytes\n", (UINT32)ddev.sectorSize());
    printf("DriveDevice capacity:     %10ld Bytes\n", (UINT32)ddev.numSectors() * (UINT32)ddev.sectorSize());

    // Construct the single surface volume object.
    NSR::SingleSurfaceVolume ssVol(&ddev);
    ssVol.fromDisk(err);
//  if (err)
//  {
//      printf("Single Suface Volume construction failed.  Error: %s\n", err.message());
//      exit(1);
//  }

    // At this point, we've successfully created our DriveDevice.
    srand48(clock());

    UINT32 nX;

    // takeLogExtent() tests

    // Loop throught the hard-coded parameter sets.
    for (nX = 0; nX < 7; nX++)
    {
        for (UINT32 nY = 0; nY < 5; nY++)
        {
            // Construct a new random bitmap.
            RandomSRBitmap* pRandBM = new RandomSRBitmap(ssVol, NSR::srSpace_UnerasedOnly, nX, nY);
            if (pRandBM == NULL)
            {
                printf("Could not create random bitmap.\n");
                exit (1);
            }

            // Make sure the bitmap is valid.
            if (!pRandBM->isValid())
                exit (1);

            // Run the test algorithm.
            if (!pRandBM->testTakeLogExtent())
                exit (1);

            // Release the random bitmap.
            delete pRandBM;
        }
    }

    // takeLogExtentList() tests

    // Loop throught the hard-coded parameter sets.
    for (nX = 0; nX < 7; nX++)
    {
        for (UINT32 nY = 0; nY < 5; nY++)
        {
            // Construct a new random bitmap.
            RandomSRBitmap* pRandBM = new RandomSRBitmap(ssVol, NSR::srSpace_UnerasedOnly, nX, nY);
            if (pRandBM == NULL)
            {
                printf("Could not create random bitmap.\n");
                exit (1);
            }

            // Make sure the bitmap is valid.
            if (!pRandBM->isValid())
                exit (1);

            // Run the test algorithm.
            if (!pRandBM->testTakeLogExtentList())
                exit (1);

            // Release the random bitmap.
            delete pRandBM;
        }
    }

    // putLogExtent() tests

    // Loop throught the hard-coded parameter sets.
    for (nX = 0; nX < 7; nX++)
    {
        for (UINT32 nY = 0; nY < 5; nY++)
        {
            // Construct a new random bitmap.
            RandomSRBitmap* pRandBM = new RandomSRBitmap(ssVol, NSR::srSpace_UnerasedOnly, nX, nY);
            if (pRandBM == NULL)
            {
                printf("Could not create random bitmap.\n");
                exit (1);
            }

            // Make sure the bitmap is valid.
            if (!pRandBM->isValid())
                exit (1);

            // Run the test algorithm.
            if (!pRandBM->testPutLogExtent())
                exit (1);

            // Release the random bitmap.
            delete pRandBM;
        }
    }

    // putLogExtentList() tests

    // Loop throught the hard-coded parameter sets.
    for (nX = 0; nX < 7; nX++)
    {
        for (UINT32 nY = 0; nY < 5; nY++)
        {
            // Construct a new random bitmap.
            RandomSRBitmap* pRandBM = new RandomSRBitmap(ssVol, NSR::srSpace_UnerasedOnly, nX, nY);
            if (pRandBM == NULL)
            {
                printf("Could not create random bitmap.\n");
                exit (1);
            }

            // Make sure the bitmap is valid.
            if (!pRandBM->isValid())
                exit (1);

            // Run the test algorithm.
            if (!pRandBM->testPutLogExtentList())
                exit (1);

            // Release the random bitmap.
            delete pRandBM;
        }
    }

    printf("\nAll tests completed successfully.\n");

    return 0;
}

