//-*-c++-*--------------------------------------------------------------
//  srTest.cpp
//   Tests the SpaceResource(s)
//
//   This program performs DESTRUCTIVE TESTING on an MO disk!!!!!
//
//      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.
//
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfAncil/ssTest/srTest.cpp,v $
// $Revision: 1.21 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/26 00:19:43 $
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>               
#include <errno.h>
#include <assert.h>
#include "nsrLib.h"
#include "nsrRSSdv.h"

//-----------------------------------------------------------------------------
// Local defines and constants
//-----------------------------------------------------------------------------

#define BYTEARRAY_MAX           49152   // 64K
#define BUFSIZE                 49152   // 256K
#define ITERATIONS              10      
#define RAGGED                  0
#define ALIGNED                 1
#define EXTALIGNED_FILES        15
#define EXTRAGGED_FILES         15
#define LISTALIGNED_FILES       20
#define LISTRAGGED_FILES        20
#define EXTALIGNED_ITERATIONS   3
#define EXTRAGGED_ITERATIONS    3
#define LISTALIGNED_ITERATIONS  3
#define LISTRAGGED_ITERATIONS   3

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

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

struct File             // A struct because I don't care about privacy
{
    // Data members

    INT32               crc;            // CRC for file data
    UINT32              size;           // In bytes
    LogExtent           *lextp;         // For extent-based allocation
    LogExtentList       *listp;         // For list-based allocation

    // Methods

    File(UINT32 len);   
    ~File();    
    void        newSize(UINT32 newsize);
    INT32       isAllocated(void) { return (lextp || listp); }
    INT32       allocExtent(SpaceResource& sr);
    INT32       allocList(SpaceResource& sr);
    void        freeExtent(SpaceResource& sr);
    void        freeList(SpaceResource& sr);
    INT32       writeData(void);
    INT32       checkData(void);
};

//-----------------------------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------------------------

static INT32    CheckSum(INT32,BYTE_PTR,UINT32);
static void     initData(void);
static INT32    testSR(void);
static INT32    testExtentAligned(INT32 testno);
static INT32    testListAligned(INT32 testno);
static INT32    testExtentRagged(INT32 testno);
static INT32    testListRagged(INT32 testno);
static INT32    getBlocksize(const LogExtentList& list);
static void     makeFiles(File **farr,INT32 num,UINT32 nbytes,INT32 aligned);

//-----------------------------------------------------------------------------
// Some global data
//-----------------------------------------------------------------------------

static BYTE_PTR data_buffer = new BYTE[BUFSIZE];
static BYTE_PTR zero_buffer = new BYTE[BUFSIZE];
static DriveDevice      *ddevp = NULL;
static PhysicalVolume   *pvolp = NULL;
static PhysExtent       *pextp = NULL;
static Partition        *partp = NULL;
static LogicalVolume    *lvolp = NULL;

//-----------------------------------------------------------------------------
// 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!!!!!!
//-----------------------------------------------------------------------------


INT32 main (INT16 argc, CHAR_PTR argv[])
{
    String      *str;
    INT32       rc;

    // Prepare a String variable for use with the DriveDevice constructor.

    if(argc != 2) {
        str = new String(DEFAULT_DEVFILE);
        (void) printf("WARNING:  Using default devfile %s as argument!\n",
                      DEFAULT_DEVFILE);
    } else {
        str = new String(argv[1]);
    }
    if(str == NULL) {
        (void) fprintf(stderr,"ddevp construction failed:  no free store!\n");
        exit(1);
    }

    ddevp = new DriveDevice(*str);
    if(ddevp == NULL) {
        (void) fprintf(stderr,"ddevp construction failed:  no free store!\n");
        exit(1);
    }
    if(ddevp->error() != error_None) {
        (void) fprintf(stderr,"ddevp construction failed, error = %ld\n",
                       (INT32) ddevp->error());
        delete ddevp;
        delete str;
        exit(1);
    }
    printf("DriveDevice sector size:  %ld\n",ddevp->sectorSize());
    printf("DriveDevice capacity:  %ld\n",ddevp->numSectors());

    // At this point, we've successfully created our DriveDevice.

    initData();
    rc = testSR();
    if(rc == 0) {
        printf("\nPassed all tests\n");
    } else {
        printf("\nFAILED %ld test(s)!\n",rc);
    }

    // Clean up

    delete str;
    delete ddevp;

    return rc;
}

//-----------------------------------------------------------------------------
// testSR():  The actual test.  This function actually just calls all of the
//            sub-tests in sequence.  All sub-tests are assumed to return 0
//            on success and -1 on failure.
//-----------------------------------------------------------------------------

static INT32
testSR(void)
{
    INT32 rval = 0;

#ifndef NO_TEST1
    rval += testExtentAligned(1);
#endif
#ifndef NO_TEST2
    rval += testListAligned(2);
#endif
#ifndef NO_TEST3
    rval += testExtentRagged(3);
#endif
#ifndef NO_TEST4
    rval += testListRagged(4);
#endif

    return -rval;
}

//-----------------------------------------------------------------------------
// testExtentAligned():  Tests put/takeLogExtent() methods, using 
//                       block-aligned allocation lengths.
//
// Returns 0 on success, -1 on failure.
//-----------------------------------------------------------------------------

static INT32
testExtentAligned(INT32 testno) {
    INT32               i;
    INT32               rval = 0;
    INT32               which;
    File *              filearr[EXTALIGNED_FILES];
    Error               err(error_None);
    LELSpaceResource    sr(*partp->logVolumep(),
                           srSpace_UnerasedOnly);
    if (err = sr.error()) {
        printf("ABORTING. . .sr.error() = %s\n",err.message());
        return -1;
    }

    printf("\n***** Test %ld: Extent-Based Allocation, Block-Aligned *****\n\n",
           testno);

    // We need to create a SpaceResource and some "files."  For this test, 
    // our allocation space will be BUFSIZE bytes long and we'll use 
    // EXTALIGNED_FILES files.

    LogExtent lext(partp,0,BUFSIZE);
    sr.putLogExtent(lext,err);
    if (err) {
        printf("ABORTING. . sr.putLogExtent() -> %s\n",err.message());
        return -1;
    }

    if(sr.availBytes() != BUFSIZE) {
        printf("ABORTING. . .availBytes() != BUFSIZE!!!\n");
        return -1;
    }
    assert(sr.availBytes() == BUFSIZE);

    // Randomly distribute lengths of the files over BUFSIZE bytes.  This
    // call allocates the File structs, but does not allocate any space to
    // them yet.

    makeFiles(filearr,EXTALIGNED_FILES,BUFSIZE,ALIGNED);

    for(i = 0; i < EXTALIGNED_FILES; i += 1) {
        printf("Allocating file of size %ld bytes. . .",filearr[i]->size);
        if(filearr[i]->allocExtent(sr)) {
            printf("FAILED!\n");
            rval = -1;
        } else {
            printf("succeeded\n");

            // Now write the file's data to disk.
            if(filearr[i]->writeData()) { 
                printf("DATA WRITE failed!\n");
                rval = -1;
            }
            if(filearr[i]->checkData()) {
                printf("DATA VERIFY failed!\n");
                rval = -1;
            }
        }
    }

    assert(sr.availBytes() == 0);

    for(i = 0; i < EXTALIGNED_ITERATIONS; i++) {
        printf("\n***** Iteration %ld *****\n",i);
        // Free about five contiguous files at random.
        INT32   j;
        INT32   space = 0;
        File    *freed[5];
        File    *fptr;
        
        which = lrand48() % (EXTALIGNED_FILES - 5);
        for(j = 0; j < 5; j++) {
            fptr = filearr[which+j];
            printf("Freeing file %ld\n",which+j);
            if(fptr->checkData()) {
                printf("Data verification failed on file %ld\n",which+j);
                rval = -1;
            }
            fptr->freeExtent(sr);
            freed[j] = fptr;            // So we can reallocate them later
            space += fptr->size;
        }
        printf("Freed %ld bytes. . .\n",space);

        assert(sr.availBytes() == space);

        // Now allocate some files into the pool.  

        File    *tempfiles[5];
        makeFiles(tempfiles,5,space,ALIGNED);
        for(j = 0; j < 5; j++) {
            UINT32 itmp;
            itmp = sr.maxExtLen();
            if(tempfiles[j]->size > itmp) {
                tempfiles[j]->newSize(itmp);
            }
            if(tempfiles[j]->allocExtent(sr)) {
                 printf("alloc FAILED!\n");
                 rval = -1;
                 break;
            }
            if(tempfiles[j]->writeData()) {
                printf("DATA WRITE failed!\n");
                rval = -1;
                break;
            }
        }

        // Now free those files, verifying their data

        for(j = 0; j < 5; j++) {
            if(tempfiles[j]->checkData()) {
                printf("DATA VERIFY failed!\n");
                rval = -1;
                break;
            }
            tempfiles[j]->freeExtent(sr);
        }

        assert(sr.availBytes() == space);

        // Finally, reallocate the original five files and rewrite their data

        for(j = 0; j < 5; j++) {
            printf("j = %ld, sr.availBytes() = %ld\n",j,sr.availBytes());
            if(freed[j]->allocExtent(sr)) {
                printf("FAILURE:  original file realloc failed!\n");
                printf("freed[%ld].size = %ld\n",j,freed[j]->size);
                rval = -1;
            }
            if(freed[j]->writeData()) {
                printf("FAILURE:  original file data write failed\n");
                printf("freed[%ld].size = %ld\n",j,freed[j]->size);
                rval = -1;
            }
            delete tempfiles[j];
        }
    }

    // Clean up:  delete files

    for(i = 0; i < EXTALIGNED_FILES; i++) {
        delete filearr[i];
    }

    return rval;
}

//-----------------------------------------------------------------------------
// testListAligned():  Tests put/takeLogExtentList() methods, using 
//                       block-aligned allocation lengths.
//
// Returns 0 on success, -1 on failure.
//-----------------------------------------------------------------------------

static INT32
testListAligned(INT32 testno)
{
    INT32               i;
    INT32               rval = 0;
    INT32               which;
    File *              filearr[LISTALIGNED_FILES];
    Error               err(error_None);
    LELSpaceResource    sr(*partp->logVolumep(),
                           srSpace_UnerasedOnly);
    if (err = sr.error()) {
        printf("ABORTING. . .sr.error() = %s\n",err.message());
        return -1;
    }

    printf("\n***** Test %ld: List-Based Allocation, Block-Aligned *****\n\n",
           testno);

    // We need to create a SpaceResource and some "files."  For this test, 
    // our allocation space will by BUFSIZE bytes long and we'll use 
    // EXTALIGNED_FILES files.

    LogExtent lext(partp,0,BUFSIZE);
    sr.putLogExtent(lext,err);
    if (err) {
        printf("ABORTING. . .sr.putLogExtent() -> %s\n",err.message());
        return -1;
    }
    if(sr.availBytes() != BUFSIZE) {
        printf("ABORTING. . .availBytes() != BUFSIZE!!!\n");
        return -1;
    }

    // Randomly distribute lengths of the files over BUFSIZE bytes.  This
    // call allocates the File structs, but does not allocate any space to
    // them yet.

    makeFiles(filearr,LISTALIGNED_FILES,BUFSIZE,ALIGNED);

    for(i = 0; i < LISTALIGNED_FILES; i++) {
        printf("Allocating file of size %ld bytes. . .",filearr[i]->size);
        if(filearr[i]->allocList(sr)) {
            printf("FAILED!\n");
            rval = -1;
        }
        else {
            printf("succeeded\n");

            // Now write the file's data to disk.

            if(filearr[i]->writeData()) { 
                rval = -1;
                printf("DATA WRITE failed!\n");
            }
        }
    }

    // Free groups of files randomly, then allocate some files into that
    // pool.

    srand48(23);
    for(i = 0; i < LISTALIGNED_ITERATIONS; i++) {
        printf("\n***** Iteration %ld *****\n",i);
        // Free about five files at random.
        INT32   j;
        INT32   space = 0;
        File    *freed[5];
        File    *fptr;
        for(j = 0; j < 5; j++) {
            do {
                which = lrand48() % LISTALIGNED_FILES;
                fptr = filearr[which];
            } while(!(fptr->isAllocated()));
            printf("Freeing file %ld\n",which);
            if(fptr->checkData()) {
                printf("Data verification failed on file %ld\n",which);
                rval = -1;
            }
            fptr->freeList(sr);
            freed[j] = fptr;            // So we can reallocate them later
            space += fptr->size;
        }
        printf("Freed %ld bytes. . .\n",space);

        // Now allocate some files into the pool.  

        File    *tempfiles[5];
        makeFiles(tempfiles,5,space,ALIGNED);
        for(j = 0; j < 5; j++) {
            if(tempfiles[j]->allocList(sr)) {
                 printf("alloc FAILED!\n");
                 rval = -1;
                 break;
            }
            if(tempfiles[j]->writeData()) {
                printf("DATA WRITE failed!\n");
                rval = -1;
                break;
            }
        }

        // Now free those files, verifying their data

        for(j = 0; j < 5; j++) {
            if(tempfiles[j]->checkData()) {
                printf("DATA VERIFY failed!\n");
                rval = -1;
                break;
            }
            tempfiles[j]->freeList(sr);
        }

        // Finally, reallocate the original five files and rewrite their data

        for(j = 0; j < 5; j++) {
            if(freed[j]->allocList(sr)) {
                printf("FAILURE:  original file realloc failed!\n");
                rval = -1;
            }
            if(freed[j]->writeData()) {
                printf("FAILURE:  original file data write failed\n");
                rval = -1;
            }
            delete tempfiles[j];
        }
    }

    return rval;
}

//-----------------------------------------------------------------------------
// testExtentRagged():  Tests put/takeLogExtent() methods, using 
//                      non-block-aligned allocation lengths.
//
// Returns 0 on success, -1 on failure.
//-----------------------------------------------------------------------------

static INT32
testExtentRagged(INT32  testno)
{
    INT32               i;
    INT32               rval = 0;
    INT32               which;
    File *              filearr[EXTALIGNED_FILES];
    Error               err(error_None);
    LELSpaceResource    sr(*partp->logVolumep(),
                           srSpace_UnerasedOnly);

    if (err = sr.error()) {
        printf("ABORTING. . .sr.error() = %s\n",err.message());
        return -1;
    }
    printf("\n***** Test %ld: Extent-Based Allocation, Non-Block-Aligned *****\n\n",
           testno);

    // Fill up the space resource

    LogExtent lext(partp,0,BUFSIZE);
    sr.putLogExtent(lext,err);
    if (err) {
        printf("ABORTING. . .sr.error() = %s\n",err.message());
        return -1;
    }
    if(sr.availBytes() != BUFSIZE) {
        printf("ABORTING. . .availBytes() != BUFSIZE!!!\n");
        return -1;
    }

    // Randomly distribute lengths of the files over BUFSIZE bytes.  This
    // call allocates the File structs, but does not allocate any space to
    // them yet.

    makeFiles(filearr,EXTRAGGED_FILES,BUFSIZE,RAGGED);

    for(i = 0; i < EXTRAGGED_FILES; i++) {
        printf("Allocating file of size %ld bytes. . .",filearr[i]->size);
        if(filearr[i]->allocExtent(sr)) {
            printf("FAILED!\n");
            rval = -1;
        }
        else {
            printf("succeeded\n");

            // Now write the file's data to disk.
            if(filearr[i]->writeData()) { 
                printf("DATA WRITE failed!\n");
            }
        }
    }

    // Now free and reallocate files randomly.

    for(i = 0; i < EXTRAGGED_ITERATIONS; i++) {
        File *fptr;

        which = lrand48() % EXTRAGGED_FILES;
        fptr = filearr[which];
        printf("Freeing and reallocating file of size %ld. . .",fptr->size);

        // Verify data first

        if(fptr->checkData()) {
            printf("DATA VERIFY FAILED. . .");
        }

        fptr->freeExtent(sr);
        if(fptr->allocExtent(sr)) {
            printf("FAILED!\n");
            rval = -1;
        }
        else {
            printf("succeeded\n");
        }
    }

    // Clean up:  delete files

    for(i = 0; i < EXTRAGGED_FILES; i++) {
        delete filearr[i];
    }

    return rval;
}

//-----------------------------------------------------------------------------
// testListRagged():  Tests put/takeLogExtentList() methods, using 
//                    non-block-aligned allocation lengths.
//
// Returns 0 on success, -1 on failure.
//-----------------------------------------------------------------------------

static INT32
testListRagged(INT32    testno)
{
    INT32               i;
    INT32               rval = 0;
    INT32               which;
    File *              filearr[LISTRAGGED_FILES];
    Error               err(error_None);
    LELSpaceResource    sr(*partp->logVolumep(),
                           srSpace_UnerasedOnly);

    if (err = sr.error()) {
        printf("ABORTING. . .sr.error() = %s\n",err.message());
        return -1;
    }
    printf("\n***** Test %ld: List-Based Allocation, Non-Block-Aligned *****\n\n",
           testno);

    // We need to create a SpaceResource and some "files."  For this test, 
    // our allocation space will by BUFSIZE bytes long and we'll use 
    // LISTRAGGED_FILES files.

    LogExtent lext(partp,0,BUFSIZE);
    sr.putLogExtent(lext,err);
    if (err) {
        printf("ABORTING. . .sr.error() = %s\n",err.message());
        return -1;
    }
    if(sr.availBytes() != BUFSIZE) {
        printf("ABORTING. . .availBytes() != BUFSIZE!!!\n");
        return -1;
    }

    // Randomly distribute lengths of the files over BUFSIZE bytes.  This
    // call allocates the File structs, but does not allocate any space to
    // them yet.

    makeFiles(filearr,LISTRAGGED_FILES,BUFSIZE,RAGGED);

    for(i = 0; i < LISTRAGGED_FILES; i++) {
        printf("Allocating file of size %ld bytes. . .",filearr[i]->size);
        if(filearr[i]->allocList(sr)) {
            printf("FAILED!\n");
            rval = -1;
        }
        else {
            printf("succeeded\n");

            // Now write the file's data to disk.

            if(filearr[i]->writeData()) { 
                rval = -1;
                printf("DATA WRITE failed!\n");
            }
        }
    }

    // Free groups of files randomly, then allocate some files into that
    // pool.

    srand48(23);
    for(i = 0; i < LISTRAGGED_ITERATIONS; i++) {
        printf("\n***** Iteration %ld *****\n",i);
        // Free about five files at random.
        INT32   j;
        INT32   space = 0;
        File    *freed[5];
        File    *fptr;
        for(j = 0; j < 5; j++) {
            do {
                which = lrand48() % LISTRAGGED_FILES;
                fptr = filearr[which];
            } while(!(fptr->isAllocated()));
            printf("Freeing file %ld\n",which);
            if(fptr->checkData()) {
                printf("Data verification failed on file %ld\n",which);
                rval = -1;
            }
            fptr->freeList(sr);
            freed[j] = fptr;            // So we can reallocate them later
            space += fptr->size;
        }
        printf("Freed %ld bytes. . .\n",space);

        // Now allocate some files into the pool.  

        File    *tempfiles[5];
        makeFiles(tempfiles,5,space,RAGGED);
        for(j = 0; j < 5; j++) {
            if(tempfiles[j]->allocList(sr)) {
                 printf("alloc FAILED!\n");
                 rval = -1;
                 break;
            }
            if(tempfiles[j]->writeData()) {
                printf("DATA WRITE failed!\n");
                rval = -1;
                break;
            }
        }

        // Now free those files, verifying their data

        for(j = 0; j < 5; j++) {
            if(tempfiles[j]->checkData()) {
                printf("DATA VERIFY failed!\n");
                rval = -1;
                break;
            }
            tempfiles[j]->freeList(sr);
        }

        // Finally, reallocate the original five files and rewrite their data

        for(j = 0; j < 5; j++) {
            if(freed[j]->allocList(sr)) {
                printf("FAILURE:  original file realloc failed!\n");
                rval = -1;
            }
            if(freed[j]->writeData()) {
                printf("FAILURE:  original file data write failed\n");
                rval = -1;
            }
            delete tempfiles[j];
        }
    }
 
    return rval;
}

//-----------------------------------------------------------------------------
// CheckSum():  The usual checksum/CRC code, stolen once again from 
//              distress.c...
//-----------------------------------------------------------------------------

static INT32
CheckSum(INT32 csum,BYTE_PTR data,UINT32 size)
{
    INT32 i;

    for(i = 0; i < size; i++) {
        csum = ((unsigned)csum<<1 | (unsigned)csum>>31) ^ (unsigned)data[i];
    }
    return csum;
}

//-----------------------------------------------------------------------------
// initData():  Fill up the data array with random data, zero out the zero
//              array, and create a random LogExtentList.  
//
// The LogExtentList will contain 256K worth of data, but will be randomly
// distributed among many LogExtents.
//-----------------------------------------------------------------------------

static void
initData(void)
{
    INT32       i;
    INT32       blocksize;
    INT32       nblocks;

    // Fill up the data buffers

    srand48(23);
//    srand48(time(NULL));              // Seed random number generator
    for(i = 0; i < BUFSIZE; i++) {
        data_buffer[i] = (BYTE) (lrand48() % 256);
        zero_buffer[i] = 0;
    }

    // We need to construct several ancillary objects so that we can construct
    // LogExtents.  Each individual test will decide what kind of extents
    // and/or lists to create, so we'll just get the ancillary objects ready
    // here (primarily the Partition).

    pvolp = new PhysicalVolume(ddevp);
    pextp = new PhysExtent(pvolp,257,pvolp->numSectors() - 260);
    partp = new Partition(*pextp);
    lvolp = new LogicalVolume(partp);
    if((pvolp == NULL) || (pextp == NULL) || (partp == NULL) || (lvolp == NULL)) {
        (void) fprintf(stderr,"Construction of pvolp, pextp, partp, or lvolp failed:  \
no free store!");
        delete ddevp;
        exit(1);
    }
}

//-----------------------------------------------------------------------------
// getBlocksize():  returns the block size of the extents in the given
//                  LogExtentList.
//-----------------------------------------------------------------------------

static INT32
getBlocksize(const LogExtentList& list)
{
    Error       error;

    if(list.numExtents == 0) {          // Empty list; we have to return 0!
        return 0;
    }
    const LogExtent *   lext = list.at(0,error);

    return lext->blockSize();
}

//-----------------------------------------------------------------------------
// makeFiles():  allocates File structs and randomly distributes their sizes
//               to the given size.  In other words, each of the Files will
//               be of random size, and the sum of the lengths of all the Files
//               will be nbytes.
//-----------------------------------------------------------------------------

static void
makeFiles(File **farr,INT32 num,UINT32 nbytes,INT32 aligned)
{
    INT32       i;
    INT32       which,howmuch;
    UINT32      nblocks,blocksize;
    UINT32      *sizes;

    sizes = new UINT32[num];

    // First we fill in the sizes array relatively evenly.  The last File 
    // will get any "leftovers."  

    if(num == 0) {      // Avoid divide-by-0 traps
        return;
    }

    blocksize = partp->blockSize();
    nblocks = nbytes / blocksize;
    for(i = 0; i < num; i++) {
        sizes[i] = (nblocks / num)*blocksize;
    }
    sizes[num-1] += (nblocks % num)*blocksize;

    // To make "ragged" sizes, we subtract [0,bs - 1] from each size.  This 
    // won't affect the space in terms of blocks required by each file,
    // but it will make the byte size "ragged."

    if(aligned == RAGGED) {
        for(i = 0; i < num; i++) {
            sizes[i] -= (lrand48() % blocksize);
        }
    }

    // Now the heart of this algorithm:  each size will "pick" another size
    // in the array and either donate or steal some size.  I have no real
    // idea what kind of distribution this will produce, but it should be
    // interesting in any case.  We steal or donate only in block-multiples
    // to get the RAGGED case working (as well as for the ALIGNED case).

    for(i = 0; i < num; i++) {
        which = lrand48() % num;
        if(lrand48() % 2) {     // We'll donate
            if((sizes[i] / blocksize) == 0) {  // Nothing left
                continue;
            }
            howmuch = lrand48() % ((sizes[i] / blocksize));
            howmuch *= blocksize;
            sizes[i] -= howmuch;
            sizes[which] += howmuch;
        }
        else {                  // We'll steal
            if((sizes[which] / blocksize) == 0) {  // Nothing left
                continue;
            }
            howmuch = lrand48() % ((sizes[which] / blocksize));
            howmuch *= blocksize;
            sizes[i] += howmuch;
            sizes[which] -= howmuch;
        }
    }

    // Now allocate the File structs and fill in their sizes, and we're done.

    for(i = 0; i < num; i++) {
        farr[i] = new File(sizes[i]);
        printf("Creating file of size %ld\n",sizes[i]);
    }
    delete [] sizes;
}



//-----------------------------------------------------------------------------
// File struct methods
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// File():  constructor.
//-----------------------------------------------------------------------------

File::File(UINT32 len)
{
    // Don't exceed the length of our data buffer

    if(len > BUFSIZE) {
        len = BUFSIZE;
    }

    size = len;
    crc = CheckSum(0,data_buffer,size);
    lextp = NULL;
    listp = NULL;
}

//-----------------------------------------------------------------------------
// ~File():  destructor.
//-----------------------------------------------------------------------------

File::~File()
{
    if(lextp) {
        delete lextp;
    }
    if(listp) {
        delete listp;
    }
}

//-----------------------------------------------------------------------------
// newSize():  Change the size of the File.  Recalculate CRC.
//-----------------------------------------------------------------------------

void
File::newSize(UINT32 newsize)
{
    if(newsize > BUFSIZE) {
        newsize = BUFSIZE;
    }
    size = newsize;
    crc = CheckSum(0,data_buffer,newsize);
}

//-----------------------------------------------------------------------------
// allocExtent():  Perform extent-based allocation.  
//
// Return Values:
//
// -1 on failure, 0 on success.
//-----------------------------------------------------------------------------

INT32
File::allocExtent(SpaceResource& sr)
{
    Error       error;
    LogExtent   lext;
    
    sr.takeLogExtent(lext,size,error);
    if (error) {
        printf("allocExtent() - takeLogExtent() returned error : %s\n",
               error.message());
        return -1;
    }
    
/*  The cast won't work after we added LogicalVolume* as a member
    to SpaceResource.

    if(error != error_None) {
        TableSpaceResource *srp = (TableSpaceResource *) &sr;
        printf("File::allocExtent():  error != error_None\n");
        printf("error.msg = %s\n",error.message());
        printf("Bitmap:  \n");
#ifdef DEBUG
        srp->print(0,"",512);
#endif
        return -1;
    }
...end comment out printing.  */  
    if(lextp != NULL) {
        delete lextp;
    }
    lextp = new LogExtent(lext.partitionp(),lext.startBlock(),lext.infoLen());
    if(lextp == NULL) {
        printf("File::allocExtent():  lextp == NULL\n");
        return -1;
    }
    return 0;
}

//-----------------------------------------------------------------------------
// allocList():  Perform list-based allocation.
//
// RETURN VALUES:
//
// Returns -1 on failure, 0 on success.
//-----------------------------------------------------------------------------

INT32
File::allocList(SpaceResource& sr)
{
    Error               error;
    LogExtentList       list(sr.logVolume());

    sr.takeLogExtentList(list,size,error);
    if(error != error_None) {
        printf("allocList() - takeLogExtentList(%d) returned error : %s\n",
               size,error.message());
        return -1;
    }

    if(listp != NULL) {
        delete listp;
    }
    listp = new LogExtentList(sr.logVolume());
    if(listp == NULL) {
        return -1;
    }
    *listp = list;
    return 0;
}

//-----------------------------------------------------------------------------
// freeExtent():  Return file's extent to SpaceResource.
//-----------------------------------------------------------------------------

void
File::freeExtent(SpaceResource& sr)
{
    Error err(error_None);
    if(lextp == NULL) {
        return;
    }
    sr.putLogExtent(*lextp,err);
    if (err) {
        printf("ERROR: freeExtent() -> %s\n",err.message());
    }
    delete lextp;
    lextp = NULL;
}

//-----------------------------------------------------------------------------
// freeList():  Return file's list to SpaceResource.
//-----------------------------------------------------------------------------

void
File::freeList(SpaceResource& sr)
{
    Error err(error_None);
    if(listp == NULL) {
        return;
    }
    sr.putLogExtentList(*listp,err);
    if (err) {
        printf("ERROR: freeList() -> %s\n",err.message());
    }
    delete listp;
    listp = NULL;
}

//-----------------------------------------------------------------------------
// writeData():  writes data, if possible, to the file's extent.  Currently
//               hard-coded to use the global variable data_buffer as the
//               data source.
//
// RETURN VALUES:
//
// Returns -1 on failure, 0 on success.
//-----------------------------------------------------------------------------

INT32
File::writeData(void)
{
    INT32               rval;
    ByteArray           barr(size);
    ByteArrayRef        bref(barr);
    Error               error;

    if(!isAllocated()) {
        return -1;
    }

    bref.fromMem(data_buffer);
    if(lextp) {
        if((INT32) (rval = lextp->write(bref,error)) < (INT32) size) {
            printf("File::writeData:  write failed- error is %s\n",error.message());
            return -1;
        }
        else {
            return 0;
        }
        if(error != error_None) {
            printf("File::writeData, lextp->write returned error %s\n",
                   error.message());
        }
    }
    else {
        if(listp->write(bref,error) < (INT32)size) {
            printf("File::writeData:  write failed, error is %s\n",error.message());
            return -1;
        }
        else {
            return 0;
        }
        if(error != error_None) {
            printf("File::writeData, listp->write returned error %s\n",
                   error.message());
        }
    }
}

//-----------------------------------------------------------------------------
// checkData():  Reads in the file's data and verifies that its CRC is what
//               it's supposed to be.
//
// RETURN VALUES:
//
// Returns -1 on failure, 0 on success.
//-----------------------------------------------------------------------------

INT32
File::checkData(void)
{
    INT32               readcrc;
    ByteArray           barr(size);
    ByteArrayRef        bref(barr);
    Error               error;
    INT32               rval;

    if(!isAllocated()) {
        return -1;
    }
    bref.fromMem(zero_buffer);          // Clear it out
    if(lextp) {
        if((INT32) (rval = lextp->read(bref,error)) < (INT32) size) {
            return -1;
        }
    }
    else {
        if(listp->read(bref,error) < size) {
            return -1;
        }
    }
    readcrc = CheckSum(0,(BYTE_PTR) bref,size);
    if(readcrc != crc) {
        printf("File::checkData:  read crc 0x%08lx != expected crc 0x%08lx\n",
               readcrc,crc);
    }
    return ((readcrc == crc) ? 0 : -1);
}
