//-*-c++-*--------------------------------------------------------------
//  adtTest.cpp
//    Tests ExtentList::toADTable() and fromADTable().  
//    Makes random extentLists, toADTables them, fromADTables them back,
//    and checks that they haven't changed in the process.
//
//      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.
//
//    Usage: adtTest [devfilename [numLoops]]
//        devfilename defaults to 'tbox'
//        numLoops defaults to 5
//        Note:  If you specify numLoops, you must also specify devfilename
//
//      Makes a random length
//      (< max UINT16) extent list, full of random extents (pointing
//      to random places on disk)
//      Converts the EL to and from.
//      Checks to see that nothing got 
//      garbled in the conversion process.
//      Note:  doesn't test with various extent types, but the test in 
//      ntfTest.cpp does.  only uses LongAD
//      It tests to/fromLongADT as well as to/fromADTable.
//
//      OUTPUT:
//      Tracks the results of each pass through the to/from loop.
//      Gives the number of exts sent to and the number read from.
//      These should match, and it notes where they don't.  Also, checks
//      for equality of before and after ExtentLists.
//   
//      RETURNS 0 if SUCCESSFUL
//
// !!! Assumed single partition logical volumes
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfAncil/ssTest/adtTest.cpp,v $
// $Revision: 1.19 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/07 23:51:13 $
//

// DEBUG_PRINTOUT if not commented-out will cause the extent list contents
// to be printed.  It makes for a LONG output.
//#define DEBUG_PRINTOUT 1


#include <stdlib.h>
#include <string.h>
#include "nsrLib.h"
#include "nsrRSSdv.h"


// Set max ByteArray length.  Dependent on ByteArray implementation.  !!!
//   MAX_UINT32 value defined in nsr/nsrLib.h
//   !!! We may implement UINT64 files in a later revision.
const UINT32 maxByteArrayLen = MAX_UINT32; 

// !!! CONSTANTS to play with...
// !!! maxNumExts should be less than 65535 (Max UINT16)
// it's constrained by memory size
const UINT32 maxNumExts = 500;  // max number Exts per EList in test

void printError( Error & errout ) { 
    printf("ERROR: %s \n", (CHAR_PTR) errout.message() ); 
}

void printIfErr( Error & err ) {
    if (err) 
        printError( err );
}

void
printAndExitIfErr( CHAR_PTR header,
                   Error & err )
{
    if (err)  {
        printf( "%s" , header);
        printError( err );
        exit(-1);
    }
}

LogExtent * 
makeRandExtent(Partition * pp )
{
    // Make random extent, given a partition pointer
    // Extent length will be >= 1.
    // Makes the extent on the heap. 
    // ASSUMES:  User is responsible for deleting the LogExtent eventually

    const UINT32 maxBlks = pp->numBlocks();
    const UINT32 blkLen = lrand48() % maxBlks + 1;  // extent length, in blocks
    // Note:  This algo never makes an Extent with Length = 0.

    const UINT32 byteLen = ( blkLen * pp->blockSize());// extent length, bytes
    const UINT32 strt = (maxBlks - blkLen) ? lrand48() % (maxBlks - blkLen) : 0;// extent start
    LogExtent * ext = new LogExtent( pp, strt, byteLen );
    return ext;
}


void
makeRandEL( LogExtentList & randEL,
            Partition *  pp,
            Error & err)
{
    //Make ext list of random length, fill with random exts
    //Note:  These extents will be overlapping, unlike in a real
    //       extent list.

    UINT32 lenEL = lrand48() % maxNumExts;
    LogExtent * ep; //extent pointer

    randEL.clear();
    for (UINT32 fillEL = 0; fillEL < lenEL; fillEL++) {
        ep = makeRandExtent( pp );
        randEL.append( *ep, err );
        if (err) {
            return;
        }
    }
}

#ifndef MULTIPARTITION_LOGICAL_VOLUME
const UINT32 ASSUMED_PART_START = 257; // the partition starts sector #
#endif

INT32
main (INT16 argc, CHAR_PTR argv[])
{
    srand(time((time_t *) NULL));
    Error err;
    UINT32 numLoops;     // number of to/from loops to do

    // OPEN DEVICE 
    CONST_CHAR_PTR devFile;   // device file name 

    if ( argc > 3 ||
         argc > 1 && ((strcmp(argv[1], "-?") == 0) ||
                      (strcmp(argv[1], "-h") == 0) ||
                      (strcmp(argv[1], "-H") == 0) ||
                      (strcmp(argv[1], "/?") == 0) ||
                      (strcmp(argv[1], "/h") == 0) ||
                      (strcmp(argv[1], "/H") == 0) ) ) {
        printf( "Usage: adtTest [devfilename [numLoops]]\n");
        printf( "  devfilename defaults to 'tbox'\n");
        printf( "  numLoops defaults to 5\n");
        printf( "  Note:  If you specify numLoops, you must also specify devfilename\n" );
        return -1;
    }

    if (argc < 2) 
        devFile = "tbox";   // default 
    else
        devFile = argv[1];
    DriveDevice ddev(devFile);

    if (argc < 3) 
        numLoops = 5;       // default 
    else
        numLoops = atoi( argv[2] );

    SingleSurfaceVolume ssVol( &ddev );
    ssVol.fromDisk(err);
    printAndExitIfErr( "Error after ssVol.fromDisk(): ", err );

    //Get root ICB address from ssVol
    LogExtent rootICBExtent = ssVol.rootICBAddr( err );
    printAndExitIfErr( "Error after ssVol.rootICBAddr(): ", err );
    
    Partition * partPtr = rootICBExtent.partitionp();
    LogicalVolume * lvp = rootICBExtent.logVolumep();

    LogExtentList sourceEL(*lvp); // source extent list.  gets toADT'ed
    LogExtentList destEL(*lvp);   // destination extent list.  gets fromADT'ed
    LogExtent adtExtent( rootICBExtent ); //!!!where table was stored 
    // Note: Need to set adtExtent so that fromADTable knows
    // what Partition* or LogicalVolume* to use when making the new
    // LogExtents.
    //       Current assumption is we are only dealing with SSVols therefore
    // An easy way is to take the rootICBaddress from SSVol.  This will 
    // give us our LogVol ptr and  Part ptr.

    // DO TO/FROM ADTABLE LOOP TEST
    for (UINT32 i = 0; i < numLoops; i++) {
        printf( "Loop %ld \n", i);

        // MAKE EXTENT LIST
        // Make a random length extent list (length set by a const < maxUINT16)
        // full of random extents.
        makeRandEL( sourceEL, partPtr, err);                    

        printf( "SOURCE EXTENT LIST: ");
        printf( "%ld items\n", sourceEL.numExtents());
        printAndExitIfErr( "Error from makeRandEL(): ", err );
#ifdef DEBUG_PRINTOUT
        sourceEL.print();
        printf( "\n" );
#endif

        // ALLOCATE ADTABLE             // to length of extent list.  
        //                              // Assumes:  no overflow AED needed
        // --LONG ADS
        UINT16 numExtents = (UINT16) sourceEL.numExtents() ; 
        ADType adType = ad_Long;
        UINT16 fieldOffset = 0;
        ADTable adTable(fieldOffset, numExtents, adType); 

        // WRITE EXTLIST TO ADTABLE
        // --LONG ADS
        UINT32 elistOffset = 0;  // start writing with first extent
        adTable.fromLogExtentList(sourceEL, 
                                  elistOffset, // List index.
                                  0, // ADTable index.
                                  err);
        printAndExitIfErr( "Error from sourceEL.toADTable(): ", err );
     
        // READ EXTLIST FROM ADTABLE
        destEL.clear();
        UINT32 begin_offset=0;
        adTable.appendToLogExtentList(partPtr,
                                      destEL,
                                      MAX_UINT64,
                                      begin_offset,
                                      err);
        printf( "DEST EXTENT LIST: ");
        printf( "%ld items\n", destEL.numExtents());
#ifdef DEBUG_PRINTOUT
        destEL.print();
        printf( "\n" );
#endif
        printAndExitIfErr( "Error from destEL.fromADTable(): ", err );

        // CHECK FOR EQUALITY
        if (sourceEL != destEL) {
            printf( "ERROR: After EL.to & fromADTable, Source & Dest ELs aren't equal.\n");
            return(-1);
        } // if
        else 
            printf ("OK.  Source/Dest ELs are equal.\n");
        printf( "\n");
    } // for numLoops

    return 0;
} // main()  

