//-*-c++-*--------------------------------------------------------------
// nsrExt.cpp
//  Methods for Physical and Logical Extents
//
// (c) Copyright 1995  Hewlett-Packard Company.
// 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 Government  is
//      subject to restrictions as set forth in subdivision (b)
//      (3)  (ii)  of the Rights in Technical Data and Computer
//      Software clause at 52.227-7013.
//
//       HEWLETT-PACKARD COMPANY
//       3000 Hanover St.
//       Palo Alto, CA  94304
//
// METHODS:
//   ClassName1::methodName()
//   ClassName1::methodName()
// 
//   ClassName2::methodName()
//   ClassName2::methodName()
//  
// NOTES:
//   <Notes go here>
//
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfCore/ss/nsrExt.cpp,v $
// $Revision: 1.91 $  $Author: hmgr $
// $State: Exp $     $Locker:  $
// $Date: 96/06/21 23:35:23 $

#include "nsrLib.h"
#include <string.h>    // For memset(), see LogExtent::read()

//----------------------------------------------------------------------
// METHOD:  PhysExtent::PhysExtent(pvp,firstSec,numSec)     --DVM
// METHOD:  PhysExtent::PhysExtent(pvp,extDesc)       --DVM
//  
//  These are constructors for physical extents.
//
// ARGS:
//  PhysicalVolume * pvp IN  Pointer to physical volume containing
//                           Physical extent.
//  firstSect            IN  Sector number of first sector in extent
//  numSects             IN  number of sectors in extent
//  ---
//  extDesc              IN  ExtentDesc describing extent
//
// RETURNS: none
// 
// PRE-CONDS:
// -- Pointer to PhysicalVolume is non-NULL
// -- PhysicalVolume.sectorSize() is non-zero.
// 
//----------------------------------------------------------------------

NSR::PhysExtent::PhysExtent( PhysicalVolume * pvp,
                             UINT32 first,
                             UINT32 number ) :
        physVolumep_ ( pvp ),
        startSector_ ( first ),
        numSectors_  ( number ) {
}

void
NSR::PhysExtent::reinit( PhysicalVolume * pvp,
                         UINT32 first,
                         UINT32 number,
                         Error& err ) 
{
    physVolumep_ = pvp ;
    startSector_ = first ;
    numSectors_  = number ;
    err = error();
}

NSR::Error
NSR::PhysExtent::error() const
{
    if ( physVolumep_ == 0 )
        return error_PhysicalVolume_invalid;
    if ( physVolumep_->error() )
        return physVolumep_->error();
    if ( physVolumep_->numSectors() < startSector_ + numSectors_ )
        return error_PE_beyond_physical_volume;
    return Error(error_None);
}

// Now inline
// NSR::UINT16
// NSR::PhysExtent::sectorSize() const {
//     return physVolumep_->sectorSize();
// }

//----------------------------------------------------------------------
// METHOD:  PhysExtent::write()          --JKH
//
// Writes the PhysExtent to disk.  Return values from the underlying 
// PhysicalVolume are propagated up.
//
// ARGS: 
//
// const ByteArrayRef& arr  IN  Reference to buffer containing data to be
//                              written
// Error& error            OUT  Error to hold results of operation
//
// RETURNS:
//
// The function returns the number of bytes written.  If -1 is returned,
// then an actual I/O error occurred.  If a write past EOM/EOLM condition
// occurred, a "short count" will be returned.
//
// PRE-CONDS:
//
// --The PhysExtent was successfully constructed (non-NULL pvolp; valid
//   underlying DriveDevice object).
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument arr
//   will be written to disk and taken from the memory referred to by buf.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_No_free_store:  Could not allocate memory for buffering.
// error_Empty_path_element:  Argument buffer is invalid.
// error_Write_EOM:  Attempted to write past physical EOM.
// error_Write_EOLM:  Attempted to write past logical EOM.
// error_Drive_write:  Generic I/O failure on write.
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysExtent::write(const ByteArrayRef& arr,
                       Error& err)
{
    INT32  rval;

    err = error_None;
    if(physVolumep_ == NULL) {
        err = error_PhysicalVolume_invalid;
        return -1;
    }
    if( err = physVolumep_->checkValidity() ) {
        return -1;
    }
    rval = physVolumep_->write(*this,arr,err);
    return rval;
}

//----------------------------------------------------------------------
// METHOD:  PhysExtent::writeWOE()          --JKH
//
// Writes the PhysExtent to disk.  Return values from the underlying 
// PhysicalVolume are propagated up.  Uses write-without-erase.
//
// ARGS: 
//
// const ByteArrayRef& arr IN  Reference to buffer containing data to be
//                             written
// Error& err             OUT  Error to hold results of operation
//
// RETURNS:
//
// The function returns the number of bytes written.  If -1 is returned,
// then an actual I/O error occurred.  If a write past EOM/EOLM condition
// occurred, a "short count" will be returned.
//
// PRE-CONDS:
//
// --The PhysExtent was successfully constructed (non-NULL pvolp; valid
//   underlying DriveDevice object).
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument arr
//   will be written to disk and taken from the memory referred to by buf.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_No_free_store:  Could not allocate memory for buffering.
// error_Empty_path_element:  Argument buffer is invalid.
// error_Write_EOM:  Attempted to write past physical EOM.
// error_Write_EOLM:  Attempted to write past logical EOM.
// error_Drive_write:  Generic I/O failure on write.
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysExtent::writeWOE(const ByteArrayRef& arr,
                          Error& err)
{
    INT32  rval;

    err = error_None;
    if(physVolumep_ == NULL) {
        err = error_PhysicalVolume_invalid;
        return -1;
    }
    if( err = physVolumep_->checkValidity() ) {
        return -1;
    }
    rval = physVolumep_->writeWOE(*this,arr,err);
    return rval;
}

//----------------------------------------------------------------------
// METHOD:  PhysExtent::read()          --JKH
//
// Reads the PhysExtent from disk.  Return values from the underlying 
// PhysicalVolume are propagated up.
//
// ARGS: 
//
// const ByteArrayRef& arr  IN/OUT  Reference to buffer where data is
//                                  to be read
// Error& err               OUT     Error to contain result of operation
//
// RETURNS:
//
// The function returns the number of bytes read.  If -1 is returned,
// then an actual I/O error occurred.  If a read past EOM/EOLM
// condition occurred, a "short count" will be returned.
//
// PRE-CONDS:
//
// --The PhysExtent was successfully constructed (non-NULL pvolp;
//   valid underlying DriveDevice object).
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument
//   arr will be read from disk and stored into the memory referred to
//   by buf.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_No_free_store:  Could not allocate memory for buffering.
// error_Empty_path_element:  Argument buffer is invalid.
// error_Read_EOM:  Attempted to read past physical EOM.
// error_Read_EOLM:  Attempted to read past logical EOM.
// error_Read_blank_check:  Read failed due to a blank check.
// error_Drive_read:  Generic I/O failure on read.
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysExtent::read(const ByteArrayRef& arr,
                      Error& err)
{
    INT32  rval;

    err = error_None;
    if(physVolumep_ == NULL) {
        err = error_PhysicalVolume_invalid;
        return -1;
    }
    if( err = physVolumep_->checkValidity() ) {
        return -1;
    }
    rval = physVolumep_->read(*this,arr,err);
    return rval;
}

//-----------------------------------------------------------------------------
// METHOD:  PhysExtent::erase()                                --JKH
//
// Erases the sectors in the PhysExtent.
//
// ARGS:
//
// Error& err          OUT Error value to hold result of operation
//
// RETURNS:  Returns 0 on success, -1 on failure.
//
// PRE-CONDS:
//
// --The media is erasable (e.g., not a hard disk, not WORM)
//
// POST-CONDS:
//
// --The sectors specified by the PhysExtent will be erased.
//
// ERRORS:
//
// error_None            Function completed successfully.
// error_Bad_erase_size  Extent size is not a multiple of the sector size.
// error_Erase_failed    The erase failed due to an I/O error, or inability
//                       to put the drive in exclusive mode.
//
// NOTES:  The erase capability provided by the SCSI-2 spec for MO drives
//         only allows for erasing whole sectors.  Therefore, the argument
//         PhysExtent MUST be a multiple of sector size in length.  If this
//         condition is not met, the erase call will fail.
//-----------------------------------------------------------------------------

NSR::INT32
NSR::PhysExtent::erase(Error& err)
{
    INT32    rval;

    err = error_None;
    if(physVolumep_ == NULL) {
        return 0;
    }

    rval = physVolumep_->erase(*this,err);
    return rval;
}

// PhysExtentList

//----------------------------------------------------------------------
// 
//  METHOD:  PhysExtentList::copy_()         --JLK
//    Makes a copy of contents of input PhysExtentList 
//    and assigns to *this PhysExtentList.
//
//  ARGS:
//  const PhysExtentList & pel  IN  PhysExtentList to copy
//  Error & err      OUT Error    error
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none.  Previously, assumed that free_() had been called before calling
//  copy for operator= (but not for copy constructor).  Now, this copy_()
//  calls clear() (which calls free_() ) at the very beginning.  The 
//  penalty to the copy constructor is one function call and one conditional
//  test.
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Sets err.  If err is set to anything besides error_None, this->elist_ will
//      be cleared to empty list.
//      Else, elist_ contains pointers to newly allocated DirEntries that are
//  copies of the DEs in the input arg pel.
// 
//  ERRORS:
//  error_Null_pointer    --source pel.elist_ contains nulls.
//  error_No_free_store    --no memory to make a complete dest LEL
//  error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  User must instatiate an Error var and pass it in to us.
//  You can't call pel.copy(pel).  Similar to saying pel=pel.  Meaningless.
//  In that case, copy_() does nothing.  Just returns.
//
//  If copy_() has to return an error (other than error_None), post condition
//  is that this->elist_ will be clear()'ed.
//
//  Since we do two conditionals (one for error_Null_pointer, one for
//  error_No_free_store) in a loop, this is a fairly slow routine.
//----------------------------------------------------------------------

void
NSR::PhysExtentList::copy_( const PhysExtentList & pel,
                            Error & err) {
    PhysExtent * pep;  // pep = log extent pointer, Destination list
    PhysExtent * pelPep;   // Source list

    err = error_None;

    if (&pel != this) {
        this->clear();          // ALWAYS ERASE INPUT LOG EXTENT LIST
        elist_ = pel.elist_;   
        for (UINT32 i = 0; i < elist_.entries(); i++) {
            pelPep = pel.elist_.at(i);     // Check source ptr
            if (pelPep == NULL) {
                elist_.clear();
                err = error_Null_pointer;
                return;        
            } // if
            pep = new PhysExtent( *pelPep );
            if (pep == NULL) {
                elist_.clear();
                err = error_No_free_store;
                return;      
            } // if
            else {
                elist_.at(i) = pep;
            } // else
        } // for
    } //if
    else {  // we are being asked to do: pel = pel.  would overwrite ourselves
        // instead, do nothing to this->elist_.  just leave it as is.
    }
    return;      
} // PhysExtentList::copy_


//----------------------------------------------------------------------
//  METHOD:  PhysExtentList::free_()        --JLK
//    Deletes all objects in the list.
// 
//  ARGS:
//  none
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List will have 0 entries.
// 
//  ERRORS:
//  none  
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

void 
NSR::PhysExtentList::free_() {                 
    // deep deletes, called by destructor & copy
    while (PhysExtent * pep = elist_.get()) {
        delete pep;
    }
} // PhysExtentList::free_()

//----------------------------------------------------------------------
//  METHOD:  PhysExtentList::nullPointersError()    --JLK
//  Checks list to see if it's in the invalid state where it
//      contains null pointers 
//
//  ARGS:
//  none
//
//  RETURNS:
//  error_Null_pointer  --the contained list_ has null pointers inside.
//      --This should never happen unless someone deleted
//            --a list entry with a call like delete list_.at(i).
//      --All list_ ptrs should point to extents.
//  error_None
//       
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  returns error_Null_pointer or error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Could write a faster algo if needed.
//----------------------------------------------------------------------
NSR::Error
NSR::PhysExtentList::nullPointersError() const {
    Error err = error_None;
    for (UINT32 i = 0; i < elist_.entries(); i++) {
        if ( elist_.at(i) == NULL) {
            err = error_Null_pointer;
            return err;
        } //if
    } // for
    return err;
} // PhysExtentList::nullPointersError()


NSR::PhysExtent *
NSR::PhysExtentList::at(UINT32 i, Error & err) {    // JLK
    err = error_None;
    if (i < elist_.entries()) {
        PhysExtent * tmp = elist_.at(i);
        if (tmp != NULL) {
            return tmp;
        } // if
        else {
            err = error_Null_pointer;
            return NULL;
        } // else
    } // if
    else {
        err = error_Index_out_of_bounds;
        return NULL;
    }
} // PhysExtentList::at()

const NSR::PhysExtent *
NSR::PhysExtentList::at(UINT32 i, Error & err) const {   // JLK
    err = error_None;
    if (i < elist_.entries()) {
        PhysExtent * tmp = elist_.at(i);
        if (tmp != NULL) {
            return tmp;
        } // if
        else {
            err = error_Null_pointer;
            return NULL;
        } // else
    } // if
    else {
        err = error_Index_out_of_bounds;
        return NULL;
    }
} // PhysExtentList::at() const

void
NSR::PhysExtentList::append( const PhysExtent & pe, Error & err ) {
    err = error_None;
    PhysExtent * pep = new PhysExtent(pe);
    if (pep == NULL) {
        err = error_No_free_store;
    }
    else {
        elist_.insert(pep);
    }
}


//----------------------------------------------------------------------
// METHOD:  PhysExtentList::concat      --JLK
//  
//  Concatenates supplied PEL to this PEL.
//
// ARGS:
//  PhysExtentList & el  IN  List to be concatenated to this list.
//  Error err            OUT
//
// POST-CONDS: 
//  This PEL will have grown by the amount of the supplied PEL.
// 
// NOTES:
// ISSUE: PEL::concat() should disallow if supplied PEL is in error
// (right?) Or should we propagate the error?  DVM.
//
// NOTE:  If concat() runs out of free store, post-cond is that
// this->elist_ will have grown by as many LogExtents as we were able
// to make on the heap.
//
// FUTURE: coalescing not yet imp'd.
//
//----------------------------------------------------------------------
void
NSR::PhysExtentList::concat( const PhysExtentList &el, Error & err )
{
    err = error_None;
    PhysExtent * ep;
    const PhysExtent * source;
    UINT32 listMax = el.numExtents();
    for (UINT32 i = 0; i < listMax; i++) {
        source = el.at(i, err); // err from at() is ignored. 
        ep = new PhysExtent( *source );
        if (ep == NULL) {
            err = error_No_free_store;
            return;
        }
        else {
            elist_.insert(ep);
        }
    }
}


//----------------------------------------------------------------------
// METHOD:  PhysExtentList::remove      --DVM
//  
//  Removes and frees the entry at the supplied index location.
//
// ARGS:
//  UINT32 i IN Index of PE to be removed from the list.
//
// POST-CONDS: 
//  This PEL will have grown by the amount of the supplied PEL.
// 
// NOTES:
//  Sets error to out_of_bounds if index is not in valid range.
//
// ISSUE: PEL::concat() should disallow if supplied PEL is in error
// (right?)
//
//----------------------------------------------------------------------

void
NSR::PhysExtentList::remove(UINT32 i, Error & err)
{
    if( i < elist_.entries() ) {
        delete elist_.at(i);
        elist_.removeReference(elist_.at(i));
    } else {
        err = error_Index_out_of_bounds;
    }
}

//----------------------------------------------------------------------
// METHOD:  PhysExtentList::numSectors      --DVM
// METHOD:  PhysExtentList::numBytes      --DVM
//  
//  Returns number of Sectors or bytes in this PEL.
//
// ARGS: none
//
// RETURNS: 
//  See description.
// 
// NOTES:
//
//----------------------------------------------------------------------

NSR::UINT32
NSR::PhysExtentList::numSectors() const
{
    UINT32 totalSectors = 0;
    for (UINT32 i = 0; i < elist_.entries(); i++) {
        totalSectors += elist_.at(i)->numSectors();
    }
    return totalSectors;
}

NSR::UINT32
NSR::PhysExtentList::numBytes() const
{
    UINT32 totalBytes = 0;
    for (UINT32 i = 0; i < elist_.entries(); i++) {
        totalBytes += elist_.at(i)->numBytes();
    }
    return totalBytes;
}

// LogExtent

void
NSR::LogExtent::reinit(Partition *newpp,UINT32 start,UINT32 nBytes, Error& err) {
    partitionp_ = newpp;
    startBlock_ = start;
    infoLen_    = nBytes;
    extentType_ = Unerased;
    err = error();
}

NSR::Error
NSR::LogExtent::error() const {
    if ( extentType_ != Imaginary ) {
        if ( partitionp_ == 0 )
            return error_Partition_invalid;
        if ( partitionp_->error() )
            return partitionp_->error();
        if ( partitionp_->numBlocks() < startBlock() + numBlocks() )
            return Error(error_LE_beyond_partition);
    }
    return Error(error_None);
}

//----------------------------------------------------------------------
// METHOD:  LogExtent::infoLen(il)      --DVM
//  
//  Sets the information length for the extent.
//  This may be set to any value between blockMultLen() and
//  blockMultLen() - (blockSize()-1), inclusive. If supplied length is
//  out of range, the length is set to its minimum or maximum
//  allowable value.
//
// ARGS:
//  UINT32 newLen IN New value for infoLen().
//
// RETURNS:  new information length.
// 
// PRE-CONDS:
//  List_preconds
// 
// POST-CONDS:
//  List_postconds
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------

NSR::UINT32
NSR::LogExtent::infoLen(const UINT32 il, Error& err)
{
    UINT32 maxLen, minLen;

    if(partitionp_) {
        // If we have a partition pointer.

        // First, calculate min and max
        if ( extentType_ == Imaginary ) {
            maxLen = maxInfoLen();
            minLen = 0;
        } else {
            maxLen = blockMultLen();
            minLen = maxLen - partitionp_->blockSize() + 1;
        }
    
        // Then, set to within bounds
        if ( maxLen == 0 ) {    // minLen is negative
            infoLen_ = maxLen;
        } else {
            if ( il < minLen )
                infoLen_ = minLen;
            else if ( il > maxLen )
                infoLen_ = maxLen;
            else
                infoLen_ = il;
        }

        // If did not set it as requested, set error to illegal Len
        // Otherwise, set error based upon validity of result.
        if ( il != infoLen_ )
            err = Error(error_InfoLen_illegal);
        else
            err = error();
    } else {
        // We don't have a partition pointer.  We must set the infoLen
        //   to zero, and return an error.
        infoLen_ = 0;
        err = error_Cant_grow_null_extent;
    }
    
    // Return the resulting infoLen
    return infoLen_;
}

NSR::LogExtent::operator NSR::PhysExtent()
{
    return PhysExtent(partitionp_->physVolumep(),
                      startBlock() + partitionp_->startSector(),
                      numBlocks());
}

// ISSUE:  LE::!= add comments

NSR::Boolean
NSR::LogExtent::operator!=(const LogExtent & other) const {

    INT16 bad = 0;

    bad = (bad || (extentType() != other.extentType()));
    bad = (bad || (partitionp() != other.partitionp()));
    bad = (bad || (startBlock() != other.startBlock()));
    bad = (bad || (infoLen()    != other.infoLen()   ));

    return bad? True:False;
}

//----------------------------------------------------------------------
// METHOD:  LogExtent::write()          --JKH
//
// Writes the LogExtent to disk.  Return values from the underlying 
// Partition are propagated up.
//
// ARGS: 
//
// const ByteArrayRef& arr  IN  Reference to buffer containing data to be
//                              written
// Error& err               OUT error to hold results of operation
//
// RETURNS:
//
// The function returns the number of bytes written.  If -1 is returned,
// then an actual I/O error occurred.  If a write past EOM/EOLM condition
// occurred, a "short count" will be returned.
//
// PRE-CONDS:
//
// --The Partition was successfully constructed (non-NULL partp; valid
//   underlying DriveDevice object).
// --the length of the argument ByteArrayRef is exactly equal to the infoLen
//   of the LogExtent.  This is required to prevent having a bi-stated 
//   LogExtent (e.g., writing two sectors in a ten sector "Erased" LogExtent
//   would leave it in both a Recorded and Erased state).
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument arr
//   will be written to disk and taken from the memory referred to by buf.
// --The type of LogExtent will be changed.  If the write succeeds completely,
//   the type will be Recorded; if more than 0 bytes are written but less
//   the infoLen(), the type will be changed to Unerased.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_No_free_store:  Could not allocate memory for buffering.
// error_Empty_path_element:  Argument buffer is invalid.
// error_Write_EOM:  Attempted to write past physical EOM.
// error_Write_EOLM:  Attempted to write past logical EOM.
// error_Drive_write:  Generic I/O failure on write.
//
// ISSUE: LE::write() should error on imaginary extents
//  
// ISSUE: LE::write() should change extent types to RECORDED
//  If type is erased, can use write-without-erase
//  If type is unerased, can use normal write (with erase)
//  
//---------------------------------------------------------------------

NSR::INT32
NSR::LogExtent::write(const ByteArrayRef& arr,
                      Error& err)
{
    INT32    rval;
    PhysExtent    pext = (PhysExtent) *this;

    err = error_None;
    
    if(arr.length() < infoLen_) {
        err = error_Invalid_write_length;
        return -1;
    }
   
    if(partitionp_ == NULL) {    // Null extent case; is okay
        return 0;
    }
    if( err = partitionp_->checkValidity() ) {
        return -1;
    }
    if(extentType_ == Imaginary) {
        err = error_Cant_write_imaginary_extent;
        return -1;
    }
    switch(extentType_)
    {
    case Erased:
        rval = pext.writeWOE(arr,err);
        break;
    default:
        rval = pext.write(arr,err);
        break;
    }
    if(rval > 0) {
        if(rval < arr.length())    // Incomplete write
        {
            extentType_ = Unerased;
        }
        else
        {
            if(rval == arr.length())
            {
                extentType_ = Recorded;
            }
        }
    }
    return rval;
}

//---------------------------------------------------------------------
// METHOD:  LogExtent::read()            --JKH
//
// Reads the LogExtent from disk.  Return values from the underlying 
// Partition are propagated up.
//
// ARGS: 
//
// const ByteArrayRef& arr  IN/OUT  Reference to buffer where data is
//                                  to be stored
// Error& err               OUT     Error to hold result of operation
//
// RETURNS:
//
// The function returns the number of bytes read.  If -1 is returned,
// then an actual I/O error occurred.  If a read past EOM/EOLM
// condition occurred, a "short count" will be returned.
//
// PRE-CONDS:
//
// --The LogExtent was successfully constructed (non-NULL partp; valid
//   underlying DriveDevice object).
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument
//   arr will be read from disk and stored in the memory referred to
//   by buf. 
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_No_free_store:  Could not allocate memory for buffering.
// error_Empty_path_element:  Argument buffer is invalid.
// error_Read_EOM:  Attempted to read past physical EOM.
// error_Read_EOLM:  Attempted to read past logical EOM.
// error_Read_blank_check:  Read failed due to a blank check.
// error_Drive_read:  Generic I/O failure on read.
//---------------------------------------------------------------------

NSR::INT32
NSR::LogExtent::read(const ByteArrayRef& arr,
                     Error& err)
{
    INT32    rval;
    PhysExtent    pext = (PhysExtent) *this;

    err = error_None;
    if(partitionp_ == NULL) {    // NULL extent
        return 0;
    }
    if( err = partitionp_->checkValidity() ) {
        return -1;
    }

    // If the extent type is anything but Recorded, we simply fill the
    // user buffer with 0's.

    switch(extentType_)
    {
    case Imaginary:
    case Unerased:
    case Erased:
        (void) NSR::MemSet((BYTE_PTR) arr,0,arr.length());
        return arr.length();
        break;
    }
  
    rval = pext.read(arr,err);
    return rval;
}

//-----------------------------------------------------------------------------
// METHOD:  LogExtent::erase()                                --JKH
//
// Erases the blocks in the LogExtent.
//
// ARGS:
//
// Error& err          OUT Error value to hold result of operation
//
// RETURNS:  Returns 0 on success, -1 on failure.
//
// PRE-CONDS:
//
// --The media is erasable (e.g., not a hard disk, not WORM)
//
// POST-CONDS:
//
// --The sectors specified by the LogExtent will be erased.
//
// ERRORS:
//
// error_None            Function completed successfully.
// error_Bad_erase_size  Extent size is not a multiple of the sector size.
// error_Erase_failed    The erase failed due to an I/O error, or inability
//                       to put the drive in exclusive mode.
//
// NOTES:  The erase capability provided by the SCSI-2 spec for MO drives
//         only allows for erasing whole sectors.  Therefore, the argument
//         PhysExtent MUST be a multiple of sector size in length.  If this
//         condition is not met, the erase call will fail.
//-----------------------------------------------------------------------------

NSR::INT32
NSR::LogExtent::erase(Error& err)
{
    INT32    rval;
    PhysExtent    pext = (PhysExtent) *this;

    err = error_None;
    if(partitionp_ == NULL) {
        return 0;
    }

    rval = pext.erase(err);
    if(rval == 0) {    // Success; update extent type
        extentType_ = Erased;
    }
    return rval;
}

//----------------------------------------------------------------------
//  METHOD:  LogExtent::print()          --JLK
//    Prints LE.
//
//  ARGS:
//  indent        IN      Number of spaces to indent
//  header        IN      String to print as a header title
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  0 <= indent < 160
// 
//  POST-CONDS: (E.g., changes to class data members)
//  none
// 
//  ERRORS:
//  none  
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//----------------------------------------------------------------------

void 
NSR::LogExtent::print( UINT32 indent, const String & header) const {

    CHAR_PTR ind = (CHAR_PTR) NSR::space(indent);        // cast to CHAR_PTR

    printf("%s------- LogExtent -------\n",   ind);
    printf("%s<%s>\n",         ind, (CHAR_PTR) header);
    printf("%s partition ptr         = %p\n",    ind, partitionp() );
    String et;
    switch (extentType()) {
    case 0: et = "Imaginary"; break;
    case 1: et = "Unerased";  break;
    case 2: et = "Erased";    break;
    case 3: et = "Recorded";  break;
    }
    printf("%s extentType            = %s\n",    ind, (CONST_CHAR_PTR)et );
    printf("%s startBlock            = %lu\n",   ind, startBlock() );
    printf("%s numBlocks             = %lu\n",   ind, numBlocks() );
    printf("%s blockMultLen          = %lu\n",   ind, blockMultLen() );
    printf("%s infoLen               = %lu\n",   ind, infoLen() );
    printf("%s unusedLen             = %lu\n",   ind, unusedLen() );
}

// LogExtentList
// ISSUE:  LEL::printVerbose  add comments

void 
NSR::LogExtentList::printVerbose() {        // JLK
    NSR::LogExtentListIter leli( *this );  // would be const method exc. for iterator doesn't like const objects
    NSR::LogExtent *lep;

    UINT32 i = 0;
    if (numLogExtents() != 0) {
        for (leli.reset(); leli.next(); i++) {
            lep = leli.key();
            printf("  Extent %ld\n", i);
            UINT32 indent = 3;
            String header = "";
            lep->print( indent, header );
        } // for
    } // if
}

//----------------------------------------------------------------------
// 
//  METHOD:  LogExtentList::copy_()         --JLK
//    Makes a copy of contents of input LogExtentList 
//    and assigns to *this LogExtentList.
//
//  ARGS:
//  const LogExtentList & el  IN  LogExtentList to copy
//  Error & err      OUT error
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none.  Previously, assumed that free_() had been called before calling
//  copy for operator= (but not for copy constructor).  Now, this copy_()
//  calls clear() (which calls free_() ) at the very beginning.  The 
//  penalty to the copy constructor is one function call and one conditional
//  test.
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Sets err.  If err is set to anything besides error_None, this->elist_ will
//      be cleared to empty list.
//      Else, elist_ contains pointers to newly allocated DirEntries that are
//  copies of the DEs in the input arg lel.
// 
//  ERRORS:
//  error_Null_pointer    --source lel.elist_ contains nulls.
//  error_No_free_store    --no memory to make a complete dest LEL
//  error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  User must instatiate an Error var and pass it in to us.
//  Can't do a call like lel.copy(lel).  Similar to saying lel=lel. Meaningless.
//  In that case, copy_() does nothing.  Just returns.
//
//  If copy_() has to return an error (other than error_None), post condition
//  is that this->elist_ will be clear()'ed.
//
//  Since we do two conditionals (one for error_Null_pointer, one for
//  error_No_free_store) in a loop, this is a fairly slow routine.
//----------------------------------------------------------------------

void
NSR::LogExtentList::copy_( const LogExtentList & lel,
                           Error & err)
{
    LogExtent * lep;            // lep = log extent pointer, Destination list
    LogExtent * lelLep;         // Source list

    err = error_None;

    if (&lel != this) {
        this->clear();
        logicalVolume_ = lel.logicalVolume_;
        elist_         = lel.elist_; // Shallow copy
        for (UINT32 i = 0; i < elist_.entries(); i++) {
            lelLep = lel.elist_.at(i);     // Check source ptr
            if (lelLep == NULL) {
                elist_.clear();
                err = error_Null_pointer;
                return;      
            } // if
            lep = new LogExtent( *lelLep );
            if (lep == NULL) {
                elist_.clear();
                err = error_No_free_store;
                return;      
            } // if
            else {
                elist_.at(i) = lep;
            } // else
        } // for
    } //if
    else {  // we are being asked to do: lel = lel.  would overwrite ourselves
        // instead, do nothing to this->elist_.  just leave it as is.
    }
    return;      
} // LogExtentList::copy_


//----------------------------------------------------------------------
//  METHOD:  LogExtentList::free_()        --JLK
//    Deletes all objects in the list.
// 
//  ARGS:
//  none
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List will have 0 entries.
// 
//  ERRORS:
//  none  
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//----------------------------------------------------------------------

void 
NSR::LogExtentList::free_()
{
    // deep deletes, called by destructor & copy 
    while (LogExtent *lep=elist_.get()) {
        delete lep;
    }
} // LogExtentList::free_()

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::nullPointersError()      --JLK
//  Checks list to see if it's in the invalid state where it
//      contains null pointers 
//
//  ARGS:
//  none
//
//  RETURNS:
//  error_Null_pointer  --the contained list_ has null pointers inside.
//      --This should never happen unless someone deleted
//            --a list entry with a call like delete list_.at(i).
//      --All list_ ptrs should point to extents.
//  error_None
//       
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  returns error_Null_pointer or error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Could write a faster algo if needed.
//----------------------------------------------------------------------
NSR::Error
NSR::LogExtentList::nullPointersError() const {
    Error err = error_None;
    for (UINT32 i = 0; i < elist_.entries(); i++) {
        if ( elist_.at(i) == NULL) {
            err = error_Null_pointer;
            return err;
        } //if
    } // for
    return err;
} // LogExtentList::nullPointersError()

NSR::LogExtent *
NSR::LogExtentList::at(UINT32 i, Error & err) {   //JLK
    err = error_None;
    if (i < elist_.entries()) {
        LogExtent * tmp = elist_.at(i);
        if (tmp != NULL) {  // this check not needed if pre-cond is
            // For all pointers i in elist_, i != NULL
            // ...As written now, (!pre-cond) --> we check.
            return tmp;
        } // if
        else {
            err = error_Null_pointer;
            return NULL;
        } // else
    } // if
    else {
        err = error_Index_out_of_bounds;
        return NULL;
    }
} // LogExtentList::at()


// ISSUE:  Add LE at() comments
const NSR::LogExtent *
NSR::LogExtentList::at(UINT32 i, Error & err) const {   // JLK
    err = error_None;
    if (i < elist_.entries()) {
        LogExtent * tmp = elist_.at(i);
        if (tmp != NULL) {
            return tmp;
        } // if
        else {
            err = error_Null_pointer;
            return NULL;
        } // else
    } // if
    else {
        err = error_Index_out_of_bounds;
        return NULL;
    }
} // LogExtentList::at() const


// ISSUE: LEL::append() , concat() should do consolidation.
//   This should do the consolidation. If the extents are adjacent,
//   then they may be merged, rather than appending another entry.
//   This should probably be done by a method provided by the
//   LogExtents themselves, rather than here.
// ISSUE: LEL::append() should prevent mid-list non-sect-mult extents.
//   append() should also take care of disallowing a
//   non-sector-multiple extent from having another LogExtent
//   appended after it.

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::append()        --CSB
// 
//  Add a copy of the LogExtent passed in to the bottom of our list.
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::append( const LogExtent & ext , Error & err)
{
    err = error_None;
    // Just call insertAt() so that we can do coalescing.
    insertAt(ext, numExtents(), err);
}

void
NSR::LogExtentList::remove(UINT32 i, Error & err)
{
    if( i < elist_.entries() ) {
        delete elist_.at(i);
        elist_.removeReference(elist_.at(i));
    } else {
        err = error_Index_out_of_bounds;
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::collapse()        --GKG
//
//  Combines contiguous extents in the extent list.
//
//  ARGS:
//
//  RETURNS:
//
//  ERRORS:
//
//  NOTES:
//   It is recommended that you use the PackedLEL instead, as it does
//   collapsing of itself upon construction, and maintains the state
//   for its entire existence. Thus, you could simply assign into a
//   PackedLEL and then assign back into an LEL, and collapsing will
//   have occurred.
//----------------------------------------------------------------------

void
NSR::LogExtentList::collapse( Error& err )
{
    UINT32 maxExtNum = numLogExtents();
    if (maxExtNum-- < 2)
        return;

    UINT32 currExtNum = 0;
    LogExtent* currExt = at(currExtNum, err);
    if (err)
        return;

    while (currExtNum < maxExtNum) {
        UINT32 numBlocks = currExt->blockMultLen();
        if (currExt->infoLen() != numBlocks)
            return;

        numBlocks /= currExt->logVolumep()->blockSize();
        LogExtent* nextExt = at(currExtNum + 1, err);
        if (err)
            return;

        if (currExt->partitionp() == nextExt->partitionp() &&
            currExt->extentType() == nextExt->extentType() &&
            currExt->startBlock() + numBlocks == nextExt->startBlock() &&
            currExt->infoLen() + nextExt->infoLen() <= currExt->maxInfoLen())
        {
            currExt->reinit(currExt->partitionp(), currExt->extentType(),
                            currExt->infoLen() + nextExt->infoLen(), err);
            if (err)
                return;

            remove(currExtNum + 1, err);
            maxExtNum--;
        } else {
            currExtNum++;
            currExt = nextExt;
        }
    }
}

// ISSUE: LEL::concat() How should errors be handled?
//   Should we NOT copy if either is in error?
//   Should we propagate errors?

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::concat()        --CSB
// 
//  Append all the members of the source list to the bottom of our list.
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::concat( const LogExtentList &el, Error & err ) { 
    err = error_None;

    LogExtent       * lep;
    const LogExtent * source;

    UINT32 listMax = el.numExtents();
    for (UINT32 i = 0; i < listMax; i++) {
        source = el.at(i, err); //returned 'err' ignored
        if(!err) {
            // If we could get the extent from the source list.
            append(*source, err);
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  PackedLogExtentList::canCoalesce_()                --CSB
// 
//  Return True if the two extents can coalesce into one contiguous
//  extent, with extent1 logically inserted BEFORE extent2 (because
//  extent1's startBlock() occurs before extent2's startBlock(), and
//  extent1's blockMultLen() length EXACTLY runs to the beginning
//  of extent2.)
//
//  This is a static member function because we do not need to access
//  any LogExtentList members.
//
//  NOTE: View PackedLogExtentList::doCoalesce_() for a more complete 
//    description of the actual corner cases that will or will not coalesce.
// 
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
NSR::Boolean
NSR::PackedLogExtentList::canCoalesce_(const LogExtent& extent1, 
  const LogExtent& extent2) {

    // !!! ??? Do we want to check the extentType_?
    if (Trace::logAlgorithms()) {
        printf("Can we coalesce top startBlock: %ld, numBlocks: %ld with bottom startblock: %ld, numBlocks: %ld?  ",
               extent1.startBlock(), extent1.blockMultLen()/extent1.blockSize(),
               extent2.startBlock(), extent2.blockMultLen()/extent2.blockSize());
    }

    // Of course, the partitions must be the same.
    // !!! WARNING! We are comparing POINTERS here, to confirm we are
    //   on the same partition because there is no Partition::operator==().
    //   Is this what we want?  Is this correct? 
    //   PORTING ISSUE FOR LARGE MEMORY MODEL SEGMENT/OFFSET ARCHITECTURE
    //   (PC with MS-DOS).
    if(extent1.partitionp() == extent2.partitionp()) {
        // If these extents are on the same partition.

        if(extent2.startBlock() == 
           (extent1.startBlock() + (extent1.blockMultLen()/extent1.blockSize()))) {
            // If the spaces are contiguous (extent 1 on the top, extent2 on
            //   the bottom), assuming NO overlap, then we can coalesce.
            if (Trace::logAlgorithms()) {
                printf("YES!\n");
            }
            return True;
        } else {
            if (Trace::logAlgorithms()) {
                printf("NO!\n");
            }
        }
    }
    return False;  // These would not coalesce.
}

//----------------------------------------------------------------------
//  METHOD:  PackedLogExtentList::doCoalesce_()                 --CSB
// 
//  Grow (modify) the first extent passed in to include (append) the
//    second extent passed in.
//
//  NOTE: The second extent is ONLY APPENDED!  It will NOT be INSERTED!
//  NOTE: The first extent is MODIFIED to include the second extent!  The 
//    second extent is NOT modified.
//  NOTE: Unless the two extents EXACTLY border each other, the operation
//    FAILS.  The extents CANNOT OVERLAP, because that is a logical error
//    (no extents should ever overlap).  
//  NOTE: In the calculation of whether or not the extents border each 
//    other, WE CONSIDER THE BLOCK MULTIPLE FOR THE EXTENT.  For example,
//    if an extent has an infolen() of one byte, we still *know* that
//    one whole block must be reserved for that extent (the wasted 511
//    bytes cannot be in use by another extent, assuming a block size of
//    512).  Thus, for the purposes of growth calculation, we consider
//    the extent to be 512 bytes.
//  NOTE: If we must round the extent-to-be-grown up to a block boundary,
//    we will.  However, we don't round the appended length up to a
//    block boundary.  For example, (assuming a 512 byte block), coalescing 
//    a one-byte extent to a one-byte extent will yield a 513 byte extent 
//    (we round only the first extent up to a block multiple, to ensure 
//    contiguous space).
//
//          [.growExtent.][.appendedExtent.]  // Will SUCCEED! 
//
//          [.appendedExtent.][.growExtent.]  // Will FAIL! (insert not allowed).
//
//          [.growExtent.]  [.appendedExtent.]// Will FAIL! (not bordering)
//
//          [.growExtent.]                    // Will FAIL! (cannot overlap)
//               [.appendedExtent.....]       
//
//          [.growExtent................]     // Will FAIL! (cannot overlap)
//               [.appendedExtent.]           
//
//          [.appendedExtent.]                // Will FAIL! (cannot overlap)
//               [.growExtent....]
//
//          [.appendedExtent.]                // Will FAIL! (cannot overlap)
//          [.growExtent..........]
//
//          [.appendedExtent...............]  // Will FAIL! (cannot overlap)
//               [.growExtent......]
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void 
NSR::PackedLogExtentList::doCoalesce_(LogExtent& growExtent, 
                                      const LogExtent& appendedExtent,
                                      Error& err) {
    err = error_None;

    // NOTE: While it is (possibly) redundant to confirm that these extents
    //   can grow together, we should confirm the operation is possible before
    //   doing anything to ensure robust code.
    if(canCoalesce_(growExtent, appendedExtent)) {
        // If the operation is allowed.
  
        if (Trace::logAlgorithms()) {
            printf("Re-initialize growExtent to startBlock %ld, length %ld.\n",
                   growExtent.startBlock(),
                   growExtent.blockMultLen() + appendedExtent.infoLen());
        }
        // Now, we can simply re-initialize the growExtent to "stretch"
        //   over the additional space for the next extent.
        growExtent.reinit(
          growExtent.partitionp(),   // Keep the same partition.
          growExtent.startBlock(),   // Keep the same start block.
                                     // Our infoLen() is now our FULL
                                     //   blockMultLen(), PLUS the appended
                                     //   extent's length.
          growExtent.blockMultLen() + appendedExtent.infoLen(),   
          err);
        if (Trace::logAlgorithms()) {
            printf("Successful coalesce: startBlock %d, numBlocks: %ld, (infoLen: %ld).\n",
                   growExtent.startBlock(), 
                   growExtent.blockMultLen()/growExtent.blockSize(),
                   growExtent.infoLen());
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::maxExtentLen()                      --CSB
// 
//  Return the largest extent in this list.  Of course, if more than
//  one extent have the same (largest) length, just return that length
//  also.
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
NSR::UINT64 
NSR::LogExtentList::maxExtentLen(void) const {
    // Iterate through our list.
    UINT32 num = numLogExtents();
    UINT64 max = 0;
    Error err;
    for(UINT32 i = 0; i< num; i++)
    { // For each extent in our list.
        if(at(i, err)->infoLen() > max) {
            // If this infoLen is longer than our current max.
            max = at(i, err)->infoLen();
        }
    }
    return max;
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::insertAt()                  --CSB
// 
//  This function is the ONLY function that will force an insert into
//  the list and COALESCE with the items immediately BEFORE and AFTER
//  this item's designated position in the list (the previous item
//  at "index" is bumped to "index + 1").
//
//  This should be the FINAL PRIMATIVE FUNCTION used by all of the
//  concat(), append(), etc. operations because this is the ONLY
//  function that will COALESCE.
//
//  While this should be the ONLY function to coalesce, that behavior
//  is implemented in PackedLogExtentList::insertAt()!  This is a
//  virtual function that is over-ridden by the derived version!
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void 
NSR::LogExtentList::insertAt(const LogExtent& ext, UINT32 index, Error& err) {
    err = error_None;

    // First, we want to confirm we can insert this extent at this
    //   position in the list.
    // NOTE: We will (at most) append after the last position in the
    //   list.
    if(index > numExtents()) {
        // Of course, "index" is a UINT32 and can't be "< 0".
        err = error_Index_out_of_bounds;
    } else {
        // We are in bounds.  

        // Make a new extent just like the old one (we manage our own copy).
        LogExtent *ep = new LogExtent(ext);
        if (ep == NULL) 
            err = error_No_free_store;
        else {
            // We got the extent.  Put it at the desired list location.
            elist_.insertAt(index, ep);
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  PackedLogExtentList::insertAt()                    --CSB
// 
//  This function is the ONLY function that will force an insert into
//  the list and COALESCE with the items immediately BEFORE and AFTER
//  this item's designated position in the list (the previous item
//  at "index" is bumped to "index + 1").
//
//  This should be the FINAL PRIMATIVE FUNCTION used by all of the
//  concat(), append(), etc. operations because this is the ONLY
//  function that will COALESCE.
//
//  NOTE: If this extent will coalesce with the extent IMMEDIATELY
//    BEFORE the specified index, it will do so;  Thus, inserting
//    an extent at index 3 might put the extent at index 3, or it MIGHT
//    append index 2 (coalesce with the one before it).  Therefore,
//    you can't assume inserting at index 3 means you will ALWAYS have
//    an extent at index 3, even though you might have had 2 or 3
//    extents before the insert (the insert may coalesce the neighboring
//    extents together).
//
//  NOTE: Inserting at index 3 with only 2 extents in the list is allowed,
//    and we will merely append the extent (but will try to coalesce with the
//    extent before).
//   
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void 
NSR::PackedLogExtentList::insertAt(const LogExtent& ext, UINT32 index, Error& err) {
    err = error_None;

    // First, we want to confirm we can insert this extent at this
    //   position in the list.
    // NOTE: We will (at most) append after the last position in the
    //   list.
    if(index > numExtents()) {
        // Of course, "index" is a UINT32 and can't be "< 0".
        err = error_Index_out_of_bounds;
    } else {
        // We are in bounds.  
        if (Trace::logAlgorithms()) {
            printf("We are trying to insert-coalesce at index %ld.\n", index);
        }
        // Now, check to see if we should merely extend the LogExtent
        //   before or after us, or even join them together.
        // NOTE: We ONLY consider the neighboring extents!  Since this
        //   coalescing operation should happen with every extent insert,
        //   there should NEVER be neighboring extents in the list that
        //   need to be re-considered for coalescing (other than the 
        //   neighbors of this inserted extent).
        UINT16 coalescedFlag = 0;  // We are not coalesced yet.
        // TRY TO JOIN WITH THE LOGEXTENT AFTER!
        if(index < numExtents()) {
            // If there is an extent after this chosen extent.
            if(canCoalesce_(ext, *at(index, err))) {
                // If we can coalesce the new extent with the extent
                //   after us.
                if (Trace::logAlgorithms()) {
                    printf("Yes, can coalesce with extent after.\n");
                }
                // Make a copy of the newly inserted extent.
                LogExtent tempExtent = ext;
                // Grow this newly inserted extent with the next extent
                //   in the list.
                doCoalesce_(tempExtent, *at(index, err), err);
                // Now, set the grown extent at that index.
                LogExtent tempRemovedExtent;
                removeExtent(tempRemovedExtent, index, err);
                // Insert this bigger extent (recursively).
                insertAt(tempExtent, index, err);

                // Now, we inserted the new extent and coalesced with
                //   the following extent.
                coalescedFlag = 1;  // We are coalesced.
            }
        } 
        // TRY TO JOIN WITH THE LOGEXTENT BEFORE!
        if(index && !coalescedFlag) {
            // If there is an extent before us and we are not yet coalesced.
            //   (If we are already coalesced, then we must do a different
            //   process:  We must remove one extent from the list.  That's
            //   comming up after this "if" block, so be patient.)
            if(canCoalesce_(*at(index-1, err), ext)) {
                // If these two extents could grow together.
                // Do it.
                if (Trace::logAlgorithms()) {
                    printf("Yes, can coalesce with extent before. appending: startblock %d, length: %ld\n", ext.startBlock(), ext.infoLen());
                }
                doCoalesce_(*at(index-1, err), ext, err);
                if (Trace::logAlgorithms()) {
                    printf("We're back.\n");
                }
                coalescedFlag = 1;  // We are coalesced.
            }
        }

        if(coalescedFlag) {
            // If we just grew into the extent below us, then we should
            //   now check to see if we can coalesce with the extent above
            //   us.  If so, then we will actually remove an extent from
            //   the list (this insert joins the extents before and after
            //   it).
            if(index && (numLogExtents() < index)) {
                // If we have an extent before the inserted extent (we have
                //   "index", AND if we have an extent at the insert point
                //   "numLogExtents() < index" (we could simply have joined
                //   the extent before the insert point, and now there is
                //   no extent at index, even though we were told to insert
                //   at that spot.)

                // See if we can coalesce with the extent before us now.
                if(canCoalesce_(*at(index-1, err), *at(index, err))) {
                    // If we can join the existing neighboring extents.
                    if (Trace::logAlgorithms()) {
                        printf("This insert joins before and after extents.\n");
                    }
                    // Remove the bottom neighbor from the list.
                    LogExtent removedExtent;
                    removeExtent(removedExtent, index, err);
                    if(!err) {
                        // If we could remove the old extent.
                        doCoalesce_(*at(index-1, err), removedExtent, err); 
                    }
                }
            }
        } else {
            // We could not grow either neighboring extents with
            //   the new extent, so it is truly a newly inserted
            //   extent to our list.
            if (Trace::logAlgorithms()) {
                printf("We can't coalesce at all.\n");
            }
            // Make a new extent just like the old one (we manage our own copy).
            LogExtentList::insertAt(ext, index, err);
        }
    }
}

NSR::UINT64
NSR::LogExtentList::infoLen() const
{
    NSR::UINT64 length = 0;
    for (UINT32 i = 0; i < elist_.entries(); i++) {
        length += elist_.at(i)->infoLen();
    }
    return length;
}

// Set the infoLen() for the whole list, but it really only impacts the
//   last extent of the list.
// This function will set the infoLen() for the LogExtentList,
//   but the requested value should be within the range of the
//   one block for the blockMultLen() for the entire list.  Thus,
//   this function will merely set the new infoLen() for the
//   last extent in the list, which will NOT set the length
//   across a block boundary.
void
NSR::LogExtentList::infoLen(NSR::UINT64 byteLen, Error& err)
{
    // Set the last extent to the non-block multiple length, if it
    //   is a non-block multiple.
    // NOTE: We take the last extent, and set its infoLen() to its
    //   current infoLen() MINUS the bytes short of the block multiple
    //   that byteLen (the request) is.
    // This is deep, I hope it doesn't throw the reader.
    Error err;
    if(infoLen()) {
        // If we have non-imaginary extents.
        if((byteLen > (blockMultLen() - this->at(0,err)->blockSize())) &&
           (byteLen <= this->blockMultLen() )) {
            // If the new byteLen is within the block multiple for the
            //   entire number of blocks in this list.
            // Set the new length through modifying the infoLen() for
            //   the last extent in the list.
            // NOTE: We can ASSUME we will NEVER have a zero-length
            //   extent in a LogExtentList!  This should be a restriction
            //   on the list itself!
            at(
                this->numLogExtents() - 1, // The last extent.
                err)->infoLen(                     // Set this infoLen.
                    this->at(              // Get the old infoLen().
                        this->numLogExtents() - 1, // The last extent.
                        err)->infoLen()                    // Get this infoLen.
                            - (blockMultLen() - byteLen),  // Subtract wasted bytes.
                    err);
        }
    }
}
        
        
//----------------------------------------------------------------------
//  METHOD:  LogExtentList::takeFirstExtent()                   --CSB
// 
//  Remove the first extent from the list and return its address
//  to the caller.
//
//  NOTE: The caller is responsible for "delete"ing the extent!!
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::takeFirstExtent(LogExtent& logExtent,
                                    Error& err) {
  removeExtent(logExtent, 0, err);
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::takeLastExtent()                    --CSB
// 
//  Remove the last extent from the list and return its address
//  to the caller.
//
//  NOTE: The caller is responsible for "delete"ing the extent!!
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::takeLastExtent(LogExtent& logExtent, 
                                   Error& err) {
  // NOTE: We don't need to do any range checking here, or even confirm
  //   that there are extents in the list.  This is because an invalid
  //   number will simply return a NULL, setting err to "invalid index".
  removeExtent(logExtent, numExtents()-1, err);
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::extractHeadPartOfLogExtent()        --CSB
// 
//  Populate the LogExtent passed in as a subset of the extent at 
//  the index passed in.  The requested byteLen is rounded UP to the
//  nearest block boundary (because the remaining extent must start on
//  a block boundary).  Before the extent is returned, the infoLen()
//  is then set DOWN to the exact byte length requested.
//
//  If the existing extent does not have enough space to honor the 
//  request, err is set to error_No_free_space.  Similarly, if the 
//  existing extent only has EXACTLY the amount of space to honor the 
//  request, err is set to error_No_free_space because the user should 
//  have just called removeExtent() (there would be no extent to leave 
//  behind).
//
//  NOTE: This function WILL NOT remove an extent from the list!  It
//  will ONLY shorten one!
//
//  The LogExtent we extract comes from the HEAD part of the existing
//  extent. 
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::extractHeadPartOfLogExtent(LogExtent& logExtent,
                                               UINT32 index, 
                                               UINT64 byteLen, 
                                               Error& err) {
    if(index < numLogExtents()) {
        // If we have an extent at this index.
        // NOTE: We CANNOT get the blockSize() from the extent passed in, 
        //   because it MIGHT be a zero-length IMAGINARY extent with a block
        //   size of zero!
        // Round our request up to a block multiple.
        UINT64 blockMultLen;
        if(! numLogExtents()) {
            // If we don't have at least one extent.
            // We can't honor this request.
            err = error_No_free_space;
        } else {
            // If we have at least one LogExtent in our list.
            // Get the block size from the first LogExtent in our list.
            blockMultLen = calcBlockMultByteLen(byteLen, at(0, err)->blockSize());

            LogExtent* oldExtent = at(index, err);
            if(oldExtent->blockMultLen() > blockMultLen) {
                // If the LogExtent at this index has MORE than enough blocks
                //   to honor this request, then we can sub-divide it and return
                //   an extent as a subset.  (We don't want to shrink it to
                //   a length of zero).
                // Make another one just like the first.
                logExtent = *oldExtent;
      
                // Shorten the infoLen off the new one to the desired length.
    
                // HEY!  We CANNOT just set the infoLen! This is because
                //   the LogExtent allows the infoLen() to be set ONLY within
                //   a range that overlaps the total number of blocks in its
                //   control!  For example, an extent with 2 blocks of 512 
                //   byte blocks can be set from 513 bytes to 1024 bytes.
                // For what we want, we DO (always) want to rip off one
                //   or more blocks.
                // NOT WORK:  logExtent.infoLen(byteLen, err);
                logExtent.reinit(logExtent.partitionp(),
                                 logExtent.startBlock(),
                                 blockMultLen,
                                 err);
  
                // Now, adjust the start block and infoLen for the remaining
                //   extent so it does not overlap the extent removed.
                //WORK?: oldExtent->startBlock(
                //WORK?:   oldExtent->startBlock() + (blockMultLen/oldExtent->blockSize()));
                // Also, decrease the infoLen of the old extent.
                //WORK?: oldExtent->infoLen(oldExtent->infoLen() - blockMultLen, err);

                // Do this (it works).
                oldExtent->reinit(oldExtent->partitionp(),
                                  oldExtent->startBlock() + 
                                  (blockMultLen/(oldExtent->blockSize())),
                                  oldExtent->infoLen() - blockMultLen,
                                  err);

                err = error_None;  // Everything worked fine.
            } else {
                err = error_No_free_space;
            }
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::extractTailPartOfLogExtent()        --CSB
// 
//  Populate the LogExtent passed in as a subset of the extent at 
//  the index passed in.  The requested byteLen is rounded UP to the
//  nearest block boundary (because the remaining extent must start on
//  a block boundary).  
//
//  If the existing extent does not have enough space to honor the 
//  request, err is set to error_No_free_space.  Similarly, if the 
//  existing extent only has EXACTLY the amount of space to honor the 
//  request, err is set to error_No_free_space because the user should 
//  have just called removeExtent() (there would be no extent to leave 
//  behind).
//
//  The LogExtent we extract comes from the TAIL part of the existing
//  extent. 
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::extractTailPartOfLogExtent(LogExtent& logExtent,
                                               UINT32 index, 
                                               UINT64 byteLen,
                                               Error& err) {
    if(index < numLogExtents()) {
        // If we have an extent at this index.
        // NOTE: We CANNOT get the blockSize() from the extent passed in, 
        //   because it MIGHT be a zero-length IMAGINARY extent with a block
        //   size of zero!
        if(! numLogExtents()) {
            // If we don't have at least one extent.
            // We can't honor this request.
            err = error_No_free_space;
        } else {
            // If we have at least one LogExtent in our list.

            // Get the block size from the first LogExtent in our list.
            UINT64 blockMultLen;
            blockMultLen = calcBlockMultByteLen(byteLen, at(0, err)->blockSize());

            LogExtent* oldExtent = at(index, err);
            if(oldExtent->numBlocks() > (blockMultLen/at(0,err)->blockSize())) {
                // If the LogExtent at this index has more than enough blocks
                //   to honor this request, then we can sub-divide it and return
                //   an extent as a subset.
                // NOTE: It cannot have EXACTLY enough blocks, we MUST have
                //   at least one block left over!
                // Make another one just like the first.
                logExtent = *oldExtent;
      
                // Reinitialize the existing extent, to reflect the loss
                //   that we are extracting from the extent's tail.
                if (Trace::logAlgorithms()) {
                    printf("Before shortening last extent by %ld, length is %ld.\n", 
                           byteLen, oldExtent->infoLen());
                }
                // NOTE: We CANNOT simply shorten the infoLen()!  It will not
                //   shorten the extent across block boundaries!
                // NOT WORK: oldExtent->infoLen(oldExtent->infoLen() - byteLen, err);
                // Do this instead:  (shorten the extent across a block boundary).
                oldExtent->reinit(oldExtent->partitionp(),
                                 oldExtent->startBlock(),
                                 oldExtent->blockMultLen() - blockMultLen,
                                 err);
                if (Trace::logAlgorithms()) {
                    printf("After shortening last extent, length is %ld.\n", 
                           oldExtent->infoLen());
                }

                // Set the infoLen on the new one to the desired length, and
                //   force it to start at the new (later) start block.
                logExtent.reinit(logExtent.partitionp(),
                                 // RECALL: We should start at the first block
                                 //   AFTER the old extent (it is already fixed).
                                 oldExtent->startBlock() + 
                                     (blockMultLen/oldExtent->blockSize()),
                                 byteLen,  // The exact requested length.
                                 err);
                if (Trace::logAlgorithms()) {
                    printf("The tail's startblock is %ld, length %ld; the new extent is startblock %ld, length %ld.\n",
                           oldExtent->startBlock(),
                           oldExtent->infoLen(),
                           logExtent.startBlock(),
                           logExtent.infoLen());
                }

                err = error_None;  // Everything worked fine.
            } else {
                // We don't have MORE blocks than the request requires.
                err = error_No_free_space;
            }
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::extractLogExtentFromHead()                  --CSB
// 
//  Extract a (contiguous) LogExtent from the list, beginning a search
//  at the head of the list.
//
//  NOTE: This function is DIFFERENT from takeFirstExtent(), because
//  this function seeks to return a contiguous LogExtent of the exact
//  length specified (rounded up to a block multiple), and merely begins
//  its search at the head of the list.  If it must extract from an
//  existing extent, it will.  If it must go half-way (or all the way)
//  through the list to find an extent large enough to extract from,
//  it will.  Thus, this is merely a search-and-extract utility, exactly
//  the same as extractLogExtentFromTail(), except we start the search
//  at the head of the list.
//
//  NOTE: If the list does not have a LogExtent large enough to honor
//  the request, err is set to error_No_free_space;
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::extractLogExtentFromHead(LogExtent& logExtent,
                                             UINT64 byteLen,
                                             Error& err) {
    if (Trace::logAlgorithms()) {
        printf("Attempt to extract %ld bytes, we have %ld total space.\n",
               byteLen, infoLen());
    }
    err = error_None;
    // NOTE: We CANNOT get the blockSize() from the extent passed in, 
    //   because it MIGHT be a zero-length IMAGINARY extent with a block
    //   size of zero!
    // Round our request up to a block multiple.
    UINT64 blockMultLen;
    if(! numLogExtents()) {
        // If we don't have at least one extent.
        // We can't honor this request.
        err = error_No_free_space;
    } else {
        // If we have at least one LogExtent in our list.
        // Get the block size from the first LogExtent in our list.
        blockMultLen = calcBlockMultByteLen(byteLen, at(0, err)->blockSize());

        if (Trace::logAlgorithms()) {
            printf("Rounded %ld byte request to %ld bytes.\n", byteLen, blockMultLen);
        }
  
        UINT64 numExtents = numLogExtents();
        UINT64 i;
        for(i=0; i< numExtents; i++) {
            // For each extent in our list, starting at the head.
            if(at(i, err)->infoLen() >= blockMultLen) {
                if (Trace::logAlgorithms()) {
                    printf("Extent at index %ld is large enough. (%ld bytes).\n", 
                           i,
                           at(i, err)->infoLen());
                }
                // If we found an extent big enough to handle the request.
                if(at(i, err)->infoLen() == blockMultLen) {
                    // If the extent is exactly big enough to handle the
                    //   request, then simply return the entire extent.
                    if (Trace::logAlgorithms()) {
                        printf("Attempting to remove extent (of exact needed size).\n");
                    }
                    removeExtent(logExtent, i, err);
                    // Set the length to the exact requested length.
                    logExtent.infoLen(byteLen, err);
                } else {
                    // The existing extent is larger, and we must extract
                    //   a subset of it.
                    if (Trace::logAlgorithms()) {
                        printf("Attempting to extract part from the extent's head.\n");
                    }
                    // RECALL: This extraction will correctly set the resulting
                    //   extent to the exact byte count requested.
                    extractHeadPartOfLogExtent(logExtent, i, byteLen, err);
                }
                break;  // We are done with the loop.
            }
        }
        if(i == numExtents) {
            // If we could not find a logExtent that fit the need.
            err = error_No_free_space;
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::extractLogExtentFromTail()                  --CSB
// 
//  Extract a (contiguous) LogExtent from the list, beginning a search
//  at the tail of the list.
//
//  NOTE: This function is DIFFERENT from takeLastExtent(), because
//  this function seeks to return a contiguous LogExtent of the exact
//  length specified, and merely begins
//  its search at the tail of the list.  If it must extract from an
//  existing extent, it will.  If it must go half-way (or all the way)
//  through the list to find an extent large enough to extract from,
//  it will.  Thus, this is merely a search-and-extract utility, exactly
//  the same as extractLogExtentFromHead(), except we start the search
//  at the tail of the list.
//
//  NOTE: If the list does not have a LogExtent large enough to honor
//  the request, err is set to error_No_free_space;
// 
//  NOTE: If we extract from an existing extent, we extract from
//  the bottom portion of that extent (unlike extractLogExtentFromHead(),
//  that extracts from the head of the extent.)
//
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::extractLogExtentFromTail(LogExtent& logExtent,
                                             UINT64 byteLen,
                                             Error& err) {
    err = error_None;

    // NOTE: We CANNOT get the blockSize() from the extent passed in, 
    //   because it MIGHT be a zero-length IMAGINARY extent with a block
    //   size of zero!
    // Round our request up to a block multiple.
    UINT64 blockMultLen;
    if(! numLogExtents()) {
        // If we don't have at least one extent.
        // We can't honor this request.
        err = error_No_free_space;
    } else {
        // If we have at least one LogExtent in our list.
        // Get the block size from the first LogExtent in our list.
        blockMultLen = calcBlockMultByteLen(byteLen, at(0, err)->blockSize());

        UINT64 numExtents = numLogExtents();
        INT32 i;
        for(i=numExtents-1; i >= 0; i--) {
            // For each extent in our list, starting at the tail.
            if(at(i, err)->infoLen() >= blockMultLen) {
                // If we found an extent big enough to handle the request.
                if(at(i, err)->infoLen() == blockMultLen) {
                    // If the extent has exactly enough blocks to handle the
                    //   request, then simply return the entire extent.
                    removeExtent(logExtent, i, err);
                    // Set the extent to the exact byte count requested.
                    logExtent.infoLen(byteLen, err);

                } else {
                    // The existing extent is larger, and we must extract
                    //   a subset of it.
                    // RECALL: This extraction will correctly set the byteLen
                    //   to the exact requested length.
                    extractTailPartOfLogExtent(logExtent, i, byteLen, err);
                }
                break;  // We are done with the loop.
            }
        }
        if(i < 0) {
            // If we could not find a logExtent that fit the need.
            err = error_No_free_space;
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::extractLogExtentListFromHead()      --CSB
// 
//  Extract a LogExtentList from the list, beginning with extents at
//  the head of our list.  The extents can be any size (but all extent
//  lengths are block multiples).
//
//  NOTE: The requested byte length is rounded UP to the nearest block
//  multiple (because all extents must be of block multiples.)
//
//  NOTE: If the list does not have enough space to honor the request,
//  err is set to error_No_free_space.  If we have exactly enough
//  space, the entire list is emptied into the requested list.
// 
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::extractLogExtentListFromHead(LogExtentList& logExtentList,
                                                 UINT64 byteLen,
                                                 Error& err) {
    logExtentList.clear();  // Get rid of any old extents.
    err = error_None;
  
    // Round our request up to a block multiple.
    UINT64 blockMultLen;
    if(! infoLen()) {
        // If we don't have at least one non-imaginary extent.
        // We can't honor this request.
        err = error_No_free_space;
    } else {
        // If we have at least one LogExtent in our list.
        // Get the block size from the first LogExtent in our list.
        blockMultLen = calcBlockMultByteLen(byteLen, at(0, err)->blockSize());

        if(this->blockMultLen() < blockMultLen) {
            // If we don't have enough space to honor the request.
            err = error_No_free_space;
        } else if(this->blockMultLen() == blockMultLen) { 
            // If we have exactly the number of blocks to satisfy this request.
            logExtentList = *this; // Assign ourselves over.
            // Set the last extent to the non-block multiple length, if it
            //   is a non-block multiple.
            logExtentList.infoLen(byteLen, err);
            clear();               // Empty ourselves (we assigned over).
        } else {
            // We have more than enough space to handle the request.
    
            // Iterate through the list from the head, and add extents to
            //   the other list.  Don't go over budget.
            UINT32 i = 0;
            LogExtent removedExtent;
    
            while((logExtentList.blockMultLen() + at(i, err)->blockMultLen()) <= blockMultLen) {
                // While this next extent added to the populating LogExtentList
                //   will not get us over-budget.
                removeExtent(removedExtent, i, err);
                if(!err) {
                    // If we could remove this extent, add it to our list.
                    // NOTE: If this extent had some wasted space (was not a
                    //   block multiple), then we should grow it to use all
                    //   of its space.  (We can't have any extents in a list
                    //   that are NOT block multiples, EXCEPT the last extent.)
                    removedExtent.infoLen(removedExtent.blockMultLen(), err);
                    // Add it to our list.
                    if(!err) {
                        logExtentList.append(removedExtent, err);
                    } else {
                        if (Trace::logAlgorithms()) {
                            printf("HEY! Error removing and extent.\n");
                        }
                    }
                }
                // RECALL: Because we keep removing the element at the
                //   current index "i" from the list, we don't need to
                //   increment.  Just try the next item that hits this
                //   index when the one before it is removed.
            }
            // At this point, our list is either full or this next extent
            //   is larger than what we need to populate the new list
            //   at the requested length.
            if((logExtentList.blockMultLen() < blockMultLen) && !err) {
                // If we need a partial extent of this next extent to 
                //   complete the list.
                extractHeadPartOfLogExtent(removedExtent, 
                                           i, 
                                           byteLen - logExtentList.infoLen(), 
                                           err);
                // RECALL: This extent removed has the exact balance of
                //   the space desired.
                if(!err) 
                  logExtentList.append(removedExtent, err);
                else {
                    if (Trace::logAlgorithms()) {
                        printf("ERROR: Could not extract head of last extent in list!\n");
                    }
                }
            } else {
                // We have enough block multiples.  We shorten the length
                //   of the last extent in the list in case the user did not
                //   ask for a block multiple.
                this->infoLen(byteLen, err);
            }
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::extractLogExtentListFromTail()      --CSB
// 
//  Extract a LogExtentList from the list, beginning with extents at
//  the tail of our list.  The extents can be any size (but all extent
//  lengths are block multiples).
//
//  NOTE: The requested byte length is rounded UP to the nearest block
//  multiple (because all extents must be of block multiples.)
//
//  NOTE: If the list does not have enough space to honor the request,
//  err is set to error_No_free_space.  If we have exactly enough
//  space, the entire list is emptied into the requested list.
// 
//  ARGS:
//
//  RETURNS:
// 
//  ERRORS:
// 
//  NOTES:  
//----------------------------------------------------------------------
void
NSR::LogExtentList::extractLogExtentListFromTail(LogExtentList& logExtentList,
                                                 UINT64 byteLen,
                                                 Error& err) {
    logExtentList.clear();  // Get rid of any old extents.
    err = error_None;

    if(! infoLen()) {
        // If we don't have at least one non-imaginary extent.
        // We can't honor this request.
        err = error_No_free_space;
    } else {
        // If we have at least one LogExtent in our list.
        // Get the block size from the first LogExtent in our list.
        UINT64 blockMultLen = calcBlockMultByteLen(byteLen, at(0, err)->blockSize());

        if(this->blockMultLen() < blockMultLen) {
            // If we don't have enough space to honor the request.
            err = error_No_free_space;
        } else if(this->blockMultLen() == blockMultLen) { 
            // If we have exactly enough space to satisfy this request.
            logExtentList = *this; // Assign ourselves over.
            // We shorten the length
            //   of the last extent in the list in case the user did not
            //   ask for a block multiple.
            this->infoLen(byteLen, err);
            clear();               // Empty ourselves.
        } else {
            // We have more than enough space to handle the request.

            // Iterate through the list from the tail, and determine
            //   where we must break our list to generate a new list of
            //   the correct length.
            UINT32 i = numLogExtents() -1; // Start with the last item.
            UINT64 infoLen = 0;  // A running total.
    
            while((infoLen + at(i, err)->infoLen()) <= byteLen) {
                // While this next extent added to the populating LogExtentList
                //   will not get us over-budget.
                infoLen += at(i, err)->infoLen(); // Accumulate this length.
                i--;  // Back up to the previous extent.
            }
        
            LogExtent removedExtent; // Extent we remove from the old list.
    
            // At this point, we either know what LogExtents we need,
            //   or we need part of the current LogExtent.
            if(infoLen < byteLen) {
                // If we need a partial extent to complete the list.
                // Let's make this the first extent in the list, since we
                //   know it's the first one we need.
                extractTailPartOfLogExtent(removedExtent,
                                           i, 
                                           byteLen - infoLen,
                                           err);
                logExtentList.append(removedExtent, err);
            }
    
            // Now, we (possibly) have a first extent in our list, but
            //   we know we want all extents AFTER this one.
            i++; // Move to the next extent.
    
            while((logExtentList.infoLen() < byteLen) && !err) {
                // While we need more space in our list.
                removeExtent(removedExtent, i, err);
                if(!err) {
                    // If we could remove this extent.
                    logExtentList.append(removedExtent, err);
                }
            }
            // At this point, we should exactly have our list.
        }
    }
}

//----------------------------------------------------------------------
//  METHOD:  LogExtentList::recordedBlocks()        --CSB
// 
//  ARGS:
//
//  RETURNS:
//  The number of logical blocks recorded by all extents in this list.
// 
//  ERRORS:
//  none  
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//----------------------------------------------------------------------
NSR::UINT64
NSR::LogExtentList::recordedBlocks( void ) const 
{
    UINT64 recordedBlocks = 0;

    const LogExtent* extent;
    Error err;

    // Loop through all our extents and accumulate the recorded blocks.
    for(UINT32 i = 0; i< numLogExtents(); i++)
    { // For each extent in our list.
        extent = at(i, err);

        if(!err)
        { // If we got this extent.
            if(extent->extentType() == Recorded)
            { // If this is a recorded extent.
                recordedBlocks += extent->numBlocks();
            }
        }
    }
    return recordedBlocks;
}

// Assumes neither extent is in error.
NSR::Boolean
NSR::LogExtentList::operator!=( const LogExtentList & other ) const { //JLK
    UINT32 thisSize( this->numExtents() );
    UINT32 otherSize( other.numExtents() );

    if (thisSize != otherSize) 
    {
        if (Trace::logAlgorithms()) {
            printf("sizes are different!\n");
        }
        return True;  // true if different sizes
    }
    else {
        LogExtent *thisEntP;  // current Entry pointers
        LogExtent *otherEntP;   //  
        UINT16        notEqual;  // bad if any part is !=
        notEqual = 0;
        for (UINT32 i = 0; i < thisSize; i++) {
            thisEntP = this->elist_.at(i);
            otherEntP = other.elist_.at(i);
            notEqual += (UINT16) ( *thisEntP != *otherEntP );
            if (Trace::logAlgorithms()) {
                if(notEqual) printf("LogExtent index %ld is different!\n", i);
            }
        } // for
        //return (notEqual? True : False);  // return true if notEqual
        return (notEqual != 0 ? True : False);
    } // else
} // operator!=

//----------------------------------------------------------------------
// METHOD:  LogExtentList::read()        --JKH
//
// This method reads data from disk into the argument ByteArray
//            
// read() iterates through the LogExtentList and reads each Extent
// from disk into the argument ByteArray.
//
// ARGS:
//
// const ByteArrayRef& arr IN/OUT   Specifies where data read from
//                                  disk should be stored, and
//                                  amount
// Error& err              OUT      Error for result of operation
//
// PRE-CONDS:
//
// --The LogExtentList is not empty.
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument
//   arr will be read from disk and stored in the memory referred to
//   by buf. 
//
// NOTES:
//
// The ByteArray parameter dictates how much data is expected to be
// read. If the ExtentList does not encompass enough data, as much
// data as is available will be read and an error will be flagged.
//
// The write() and read() methods are identical in all respects except
// for the underlying call to the appropriate LogExtent write() or
// read() method, and hence if any change is made to write() or
// read(), the same change should be made to the other method.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_Read_EOM:  Attempted to read past physical EOM
// error_Read_EOLM:  Attempted to read past logical EOM
// error_Drive_blank_check:  A blank sector was encountered during the
//                           read 
// error_Drive_read:  Generic I/O failure on read
//
// ISSUE: LEL::read() must properly handle all extent types.
// Does it properly split up imaginary extents?
//----------------------------------------------------------------------

NSR::INT32
NSR::LogExtentList::read(const ByteArrayRef& arr,
                         Error& err )
{
    INT32  i;
    INT32  size;
    INT32  rval;
    INT32  nbytes;
    BYTE_PTR  bptr;

    err = error_None;
    bptr = (BYTE_PTR) arr;
    nbytes = 0;

    // We loop through the list entries sequentially.  For each
    // extent, we generate a ByteArrayRef of the appropriate offset
    // into the original ByteArray, then call the read() method for
    // the current extent. 

    for(i = 0; i < elist_.entries(); i++)
    {
        LogExtent *extent = elist_.at(i);
        size = (((nbytes + extent->blockMultLen()) < arr.length()) ? extent->blockMultLen() : (arr.length() - nbytes));
        ByteArrayRef curarr(arr.theArray(),arr.arrayLen(),
                            nbytes + arr.offset(),size);

        rval = extent->read(curarr,err);
        if(rval < 0) {      // An actual I/O error occurred.
            return -1;
        }
        nbytes += rval;
        bptr += rval;
        if(rval < curarr.length()) { // Got a short count!  Return now.
            return nbytes;
        }
        if(size < extent->blockMultLen()) { // ByteArray is out of space
            return nbytes;
        }
    }

    // If we get here and nbytes is less than the size of the
    // ByteArray, we didn't have enough data in the LogExtentList to
    // satisfy the request.

    if(nbytes < arr.length()) {
        err = error_Read_EOLM;
    }

    return nbytes;
}
//---------------------------------------------------------------------
// METHOD:  LogExtentList::write()        --JKH
//
// This method writes the data in the argument ByteArray to disk.
//            
// write() iterates through the LogExtentList and writes each Extent
// to disk using the data provided in the argument ByteArray.
//
// ARGS:
//
// const ByteArrayRef&  arr  IN   Specifies data, and amount of data,
//                                to be written.
// Error& err                OUT  Error to hold result of operation
//
// PRE-CONDS:
//
// --The LogExtentList is not empty
// 
// POST-CONDS:
//
// --The number of bytes specified in the length field of the argument
// arr will be written to disk
// NOTES:
//
// The ByteArray parameter dictates how much data is expected to be
// written. If the ExtentList does not encompass enough data, as much
// data as is available will be written and an error will be flagged.
//
// The write() and read() methods are identical in all respects except
// for the underlying call to the appropriate LogExtent write() or
// read() method, and hence if any change is made to write() or
// read(), the same change should be made to the other method.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_Write_EOM:  Attempted to write past physical EOM
// error_Write_EOLM:  Attempted to write past logical EOM
// error_Drive_write:  Generic I/O failure on write
//
// ISSUE: LEL::write() must properly handle all extent types.
// Does it properly split up imaginary extents?
// --------------------------------------------------------------------

NSR::INT32
NSR::LogExtentList::write(const ByteArrayRef& arr,
                          Error& err )
{
    INT32  i;
    INT32  size;
    INT32  rval;
    INT32  nbytes;
    CONST_BYTE_PTR bptr;

    err = error_None;
    bptr = (CONST_BYTE_PTR) arr;
    nbytes = 0;

    // We loop through the list entries sequentially.  For each
    // extent, we generate a ByteArrayRef of the appropriate offset
    // into the original ByteArray, then call the toDisk() method for
    // the current extent. 

    for(i = 0; i < elist_.entries(); i++)
    {
        LogExtent *extent = elist_.at(i);
        size = (((nbytes + extent->blockMultLen()) < arr.length()) ? extent->blockMultLen() : (arr.length() - nbytes)); 
        ByteArrayRef curarr(arr.theArray(),arr.arrayLen(),nbytes + arr.offset(),
                            size);

        rval = extent->write(curarr,err);
        if(rval < 0) {      // An actual I/O error occurred.
            return -1;
        }
        nbytes += rval;
        bptr += rval;
        if(rval < curarr.length()) {  // Got a short count!  Return now.
            return nbytes;
        }
        if(size < extent->blockMultLen()) {  // ByteArray is out of space
            return nbytes;
        }
    }

    // If we get here and nbytes is less than the size of the
    // ByteArray, we didn't have enough data in the LogExtentList to
    // satisfy the request.

    if(nbytes < arr.length()) {
        err = error_Write_EOLM;
    }

    return nbytes;
}

//----------------------------------------------------------------------
// METHOD:  LogExtentList::logBlockNo()        --JKH
//
// Returns the logical block number of the block containing the
// specified offset.
//
// ARGS:
//
// const UINT32 offset   IN  Byte offset to find 
//
// RETURNS:
//
// Returns the logical block number of the block containing the
// specified offset.  If the offset is not in the list, returns
// MAX_UINT32 (0xFFFFFFFF).
// 
// PRE-CONDS:  none
// POST-CONDS:  see return values
//
// NOTES:
//
// Example:  if the logical blocksize is 512, an offset of 512 will be 
//           in the SECOND logical block in the list. 
//
//----------------------------------------------------------------------

NSR::UINT32
NSR::LogExtentList::logBlockNo( const NSR::UINT64 offset ) const
{
    INT32  i;
    UINT64  len;
    UINT32  block;
    LogExtent  *lextp;

    // Check for an offset out of range

    if(offset > infoLen()) {
        return MAX_UINT32;
    }

    // Now walk through the list until we find the LogExtent
    // containing our offset.  ASSERTION:  the out-of-range check
    // above implies that the offset IS contained in the list!

    len = 0;

    // LOOP INVARIANT:  previous (i - 1) extent didn't contain the
    // offset The if statement is the key:  VERIFY IT!!!!!!
    // After loop, elist.at(i) points to the extent containing the
    // offset.

    for(i = 0; i < elist_.entries(); i++) {
        if((len + elist_.at(i)->infoLen()) > offset) {
            break;
        }
        len += elist_.at(i)->infoLen();
    }

    lextp = elist_.at(i);
    block = lextp->startBlock() + ((offset - len) / lextp->blockSize());

    return block;
}

//----------------------------------------------------------------------
// METHOD:  LogExtentList::HeadOf()          --JKH
//
// This method extracts the beginning of the LogExtentList up to and
// including the sector containing the byte offset, and fills in the
// current LogExtentList with the extracted (copied) portion.  The last Extent
// may be a non-block-multiple in length (infoLen).
//
// ARGS:
//
// const LogExtentList& list IN  List to take head of
//       UINT32 offset       IN  Byte offset indicating where to "chop" the
//                               list 
// Error& err                OUT Error to hold result of operation
//
// RETURNS:    none
//
// PRE-CONDS:  none
// POST-CONDS:
//
// --If the offset is >= the length of the list, the entire list will
//   be copied
// --If the list is empty, the current list will be cleared to empty.
// --The original list will be UNMODIFIED unless the original list is the
//   same as *this
//
// NOTES:
//
// --for offset < len, list.Head(offset).infoLen() = offset
//
//----------------------------------------------------------------------

void
NSR::LogExtentList::HeadOf( const LogExtentList& list,
                            UINT64 offset,
                            Error& err )
{
    INT32    i;
    UINT32    count;
    LogExtent    *lextp;
    LogExtent    lext;
      
    err = error_None;

    // We have a special case.  If the argument list is the same as the current
    // list, as from a call like:  list.HeadOf(list,1024,err), we can't just
    // clear the list and do the normal algorithm.  What we do instead is find
    // the last extent and chop off the rest.  This is somewhat nontrivial.

    if(&list == this) {        // Special case
        INT32  last = -1;      // Last extent to be retained

        count = 0;
        for(i = 0; i < list.elist_.entries(); i++) {
            lextp = list.elist_.at(i);
            if((count + lextp->infoLen()) > offset) {
                lext.reinit(lextp->partitionp(),lextp->startBlock(),
                            offset - count,err);
                lext.extentType(lextp->extentType());
                if(err) {
                    return;
                }
                count += lextp->infoLen();
                last = i;
                break;
            }
            else {
                count += lextp->infoLen();
            }
        }
        if(last > -1) {
            for(i = list.elist_.entries() - 1; i >= last; i--) {
                remove(i,err);
                if(err) {
                    return;
                }
            }
            append(lext,err);
        }
    }
    else {          // Normal case
        clear();
        count = 0;
        for(i = 0; i < list.elist_.entries(); i++) {
            lextp = list.elist_.at(i);
            if((count + lextp->infoLen()) > offset) {
                lext.reinit(lextp->partitionp(),lextp->startBlock(),
                            offset - count,err);
                lext.extentType(lextp->extentType());
                if(err) {
                    return;
                }
                append(lext, err); 
                break;
            }
            append(*lextp, err);  
            count += lextp->infoLen();
            if(count == offset) {    // We're done
                break;
            }
        }
    }
    return;
}

//-----------------------------------------------------------------------------
// METHOD:  LogExtentList::HeadXOf()        --JKH
//
// This method extracts the beginning of the LogExtentList up to but
// not including the sector containing the byte offset, and copies
// the extracted  portion into the current list.
//
// ARGS:
//
// const LogExtentList& list  IN  List to take HeadXOf.
//       UINT64 offset        IN  Byte offset indicating where to "chop" the
//                                list 
// Error& err                 OUT Error to hold result of operation
//
// RETURNS:  none
//
// PRE-CONDS:  none
// POST-CONDS:
//
// --If offset >= (len + bs) the entire list will be returned.
// --If the list is empty, the current list will be cleared to empty.
// --The original list will be UNMODIFIED unless the original list is the
//   same as *this
//----------------------------------------------------------------------

void
NSR::LogExtentList::HeadXOf( const LogExtentList& list,
                             UINT64 offset,
                             Error& err )
{
    UINT32    sectorsize; 
    LogExtent    *lextp;
    LogExtentList  emptyList(list.logicalVolume());

    err = error_None;
    if(list.elist_.entries() == 0) {     // We're an empty list!
        clear();
        return;
    }

    // Calculate sector size.  We extract an extent, and ask it how
    // big the sector size is.

    lextp = list.elist_.at(0);
    if(lextp == NULL) {
        return;
    }
    sectorsize = lextp->blockSize();
    if(offset < sectorsize) {
        clear();
        return;
    }
    HeadOf(list,offset - sectorsize,err);
}

//----------------------------------------------------------------------
// METHOD:  LogExtentList::TailOf()        --JKH
//
// This method extracts the end of the argument LogExtentList up to and
// including the sector containing the byte offset, and copies the
// extracted portion into the current list.
//
// ARGS:
//
// const LogExtentList& list IN  List to copy 
//       UINT64 offset       IN  Byte offset indicating where to "chop" the
//                               list 
//       Error& err          OUT Error to hold result of operation
//
// RETURNS:  none
//
// PRE-CONDS:  none
// POST-CONDS:
//
// --If offset < bs, the entire list will be returned
// --If offset >= len, or the list is empty, the current list will be
//   cleared
// --The original list will be UNMODIFIED unless the list is passed to
//   itself (e.g., list.Tail(list,offset,err) )
//----------------------------------------------------------------------

void
NSR::LogExtentList::TailOf( const LogExtentList&  list,
                            UINT64 offset,
                            Error& err )
{
    INT32    i;
    UINT32    count;
    LogExtentList  newList(list.logicalVolume());
    LogExtent    *lextp;

    // In the first part of the for loop, we just iterate through the
    // list until we've reached the byte location we're looking for.

    err = error_None;
    count = 0;
    for(i = 0; i < list.elist_.entries(); i++) {
        lextp = list.elist_.at(i);
        if(lextp == NULL) {
            return;
        }

        // Once we're into the region of the Tail, we just iterate
        // through the rest of the extents and fill up our new extent
        // list.  The first extent is special:  if the Extent is
        // imaginary, we don't change the starting address.  All the
        // other extents get copied intact.

        if((count + lextp->infoLen()) > offset) {
            // RECALL:  Extents are 30-bit limited (high 2 bits for
            //   type), so we don't need a UINT64.
            UINT32  extlen;

            extlen = (lextp->infoLen() - 
                      ((offset - count) / lextp->blockSize())
                      *lextp->blockSize());
            if(lextp->extentType() == Imaginary) {
                LogExtent firstExt(lextp->partitionp(),extlen);
                firstExt.extentType(lextp->extentType());
                newList.append(firstExt, err);  
                if(err) {
                    return;
                }
            }
            else {
                LogExtent firstExt(lextp->partitionp(),
                                   list.logBlockNo(offset),extlen);
                firstExt.extentType(lextp->extentType());
                newList.append(firstExt, err);
                if(err) {
                    return;
                }
            }

            // Now that we've made our first extent, just go through
            // the rest of the list and copy them intact.

            i++;
            while(i < list.elist_.entries()) {
                lextp = list.elist_.at(i);
                newList.append(*lextp, err);    
                if(err) {
                    return;
                }
                i++;
            }
            break;
        }
        count += lextp->infoLen();
    }

    // We've created our new list.  Now nuke the old one and equate them.

    clear();
    // elist_ = newList.elist_;
    for(i = 0; i < newList.elist_.entries(); i++) {
        lextp = newList.elist_.at(i);
        append(*lextp,err);
        if(err) {
            return;
        }
    }
}

//----------------------------------------------------------------------
// METHOD:  LogExtentList::TailXOf()        --JKH
//
// This method extracts the end of the LogExtentList up to but not
// including the sector containing the byte offset, and sets the current
// list equal to extracted portion as a new (copied) LogExtentList. 
//
// ARGS:
//
// const LogExtentList& list  IN  List to copy
//       UINT64 offset        IN  Byte offset indicating where to "chop" the
//                                list 
// Error& err                 OUT Error to hold result of operation
//
// RETURNS:  none
//
// PRE-CONDS:  none
// POST-CONDS:
//
// --If offset >= (len - bs), or the list is empty, the current list will
//   be cleared
// --The original list will be UNMODIFIED unless the list is passed to 
//   itself.
//----------------------------------------------------------------------

void
NSR::LogExtentList::TailXOf( const LogExtentList& list,
                             UINT64 offset,
                             Error& err )
{
    UINT32    sectorsize; 
    LogExtent    *lextp;
    LogExtentList  emptyList(list.logicalVolume());

    err = error_None;
    if(list.elist_.entries() == 0) {     // We're an empty list!
        clear();
        return;
    }

    // Calculate sector size.  We extract an extent, and ask it how
    // big the sector size is.

    lextp = list.elist_.at(0);
    if(lextp == NULL) {
        return;
    }
    sectorsize = lextp->blockSize();
    if (offset < sectorsize && lextp->infoLen() <= sectorsize) {
        clear();
        return;
    }
    
    //    if(offset < sectorsize) {    // this check is erroneous!
    //  clear();
    //  return;
    //}
    TailOf(list,offset + sectorsize,err);
}

//----------------------------------------------------------------------
// METHOD:  LogExtentList::print()        --JKH
//
// For debugging purposes.  Prints to stdout some minimal information about
// each extent in the list.
//
// ARGS:        
//
// UINT32 indent  IN  number of spaces to indent
// const String& header IN  header string
// RETURNS:     none
// PRE-CONDS:   none
// POST-CONDS:  none
//
// ISSUE:  LogExtentList::print():  Not currently using indent parameter.
//----------------------------------------------------------------------

void
NSR::LogExtentList::print(const UINT32 indent,const String& header) const
{
    INT32  i;

    printf("%s\n",(CHAR_PTR) header);
    for(i = 0; i < elist_.entries(); i++) {
        LogExtent  *lextp;

        lextp = elist_.at(i);
        printf("Extent %ld:  start = %ld, infoLen = %ld ",
               i,lextp->startBlock(),lextp->infoLen());
        switch(lextp->extentType())
        {
        case Imaginary:
            printf("Imaginary\n");
            break;
        case Unerased:
            printf("Unerased\n");
            break;
        case Erased:
            printf("Erased\n");
            break;
        case Recorded:
            printf("Recorded\n");
            break;
        default:
            printf("UNKNOWN EXTENT TYPE!!!!!\n");
            break;
        }
    }
}

//----------------------------------------------------------------------
// METHOD:  LogExtentList::blockMultLen        --JKH
//
// Returns the number of bytes in the LogExtentList.  Note that this
// number differs from infoLen in that the total number of bytes
// logically occupied by the extent is returned--i.e., it will be a
// block multiple in length.
//
// ARGS:   none
//
// RETURNS:     see above
// PRE-CONDS:   none
// POST-CONDS:  see above
// ERRORS:      none
//
// ISSUE: LogExtentList::blockMultLen(): returns UINT64, change to UINT32?
//----------------------------------------------------------------------

NSR::UINT64 
NSR::LogExtentList::blockMultLen() const 
{
    UINT32   i;
    NSR::UINT64   bytes = 0;
    for(i = 0; i < elist_.entries(); i++) {
        bytes += elist_.at(i)->blockMultLen();
    }
    return bytes;
}

//----------------------------------------------------------------------
//  METHOD:  PhysExtentList::at()        --JLK
//  METHOD:  LogExtentList::at()        --JLK
//    Returns ptr to extent at given location in list.
//
//  ARGS:
//  i          IN      index. 0 <= i < elist_.entries()
//  err          OUT      error
//
//  RETURNS:
//  Ptr to extent at location i in the list.
//  Or, returns NULL if index is out of bounds or if pointer is NULL.
// 
//  PRE-CONDS:  (E.g., assumes)
//  0 <= i < elist_.entries() 
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Returns non-null ptr.
//  Or, returns NULL if error condition.
// 
//  ERRORS:
//  error_Index_out_of_bounds  
//  error_Null_pointer
//  error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  To see if there is an error, check error parameter after return
//  or check to see if return value == NULL.  
// 
//  Algo note:  We could code this to be quicker if we took out the 
//  error_Null_pointer if() statement, but then error would return
//  as error_None for that case.  This is probably okay.
//  For now, the code is written on the safe side (and slow side).
// 
//  These notes are for DirList::at() but they are the same for
//  the extentList::at() methods.
// 
//  Note diff betw DirList dl; dl.at(i, error) and dl.dlist_.at(i)
//  The diff betw our implentation of at() and RogueWave's is that
//  we set an explicit error of our Error object, while they throw an
//  exception of type TOOL_INDEX.
//  
//  Example code:
//  DirList dl;    // dl is a DirList
//  dl.at(i, error);  // at() is in dl's public interface.  HP's at().
//  dl.dlist_.at(i);  // dlist_ is an RWGSlist private to dl.  RW's at().
//
//  Fct is INLINE.  Code's in .h file.
//----------------------------------------------------------------------
