1527 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1527 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* o4opt.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */
 | 
						|
 | 
						|
#include "d4all.h"
 | 
						|
#ifdef __TURBOC__
 | 
						|
   #pragma hdrstop
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef S4OPTIMIZE_OFF
 | 
						|
 | 
						|
static int opt4blockAdd( OPT4BLOCK *, FILE4 *, unsigned, long, long ) ;
 | 
						|
 | 
						|
static OPT4BLOCK *opt4fileChooseBlock( FILE4 * ) ;
 | 
						|
/* puts block onto bottom of lru-list and assigns the optlist to the block */
 | 
						|
#define opt4listLruBottomPlace( o4, b4 ) ( l4add( &((o4)->list), &((b4)->lruLink) ), ( (b4)->optList = (o4) ) )
 | 
						|
static void opt4listLruBottomShift( OPT4BLOCK * ) ;
 | 
						|
static void opt4fileReadSpBuffer( FILE4 *, const unsigned long, int, int ) ;
 | 
						|
static void opt4timeReset( OPT4 *, int, int ) ;
 | 
						|
#ifdef S4ADVANCE_TEST
 | 
						|
   S4EXPORT unsigned S4FUNCTION opt4fileReadFile( FILE4 S4PTR *, const long, char S4PTR * ) ;
 | 
						|
#else
 | 
						|
   static unsigned opt4fileReadFile( FILE4 *, const long, char * ) ;
 | 
						|
#endif
 | 
						|
 | 
						|
/* moves the block from the current position to the lru position (l4last)
 | 
						|
   of the lru-list it is currently on.  Also updates the accessCount */
 | 
						|
static void opt4listLruBottomShift( OPT4BLOCK *block )
 | 
						|
{
 | 
						|
   OPT4 *opt ;
 | 
						|
   LINK4 *link ;
 | 
						|
   LIST4 *list ;
 | 
						|
 | 
						|
   opt = &block->file->codeBase->opt ;
 | 
						|
 | 
						|
   list = &block->optList->list ;
 | 
						|
   link = (LINK4 *)&block->lruLink ;
 | 
						|
   if ( link != list->lastNode )
 | 
						|
   {
 | 
						|
      if ( list->selected == link )
 | 
						|
         list->selected = (LINK4 *)l4prev( list, link ) ;
 | 
						|
      l4remove( list, link ) ;
 | 
						|
      l4add( list, link ) ;
 | 
						|
   }
 | 
						|
 | 
						|
   if ( (unsigned int)(opt->accessTimeCount - block->accessTime) > opt->minAccessTimeVariation )
 | 
						|
      block->accessTime = opt->accessTimeCount++ ;
 | 
						|
   if ( opt->accessTimeCount == 0 )  /* time count got reset, so reset for everyone */
 | 
						|
      opt4timeReset( opt, 0, 1 ) ;
 | 
						|
 | 
						|
   block->hitCount += block->file->hitCountAdd ;
 | 
						|
}
 | 
						|
 | 
						|
static int opt4blockAdd( OPT4BLOCK *block, FILE4 *file, unsigned blockLen, long hashVal, long position )
 | 
						|
{
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 || block == 0 || hashVal < 0 || position < 0 )
 | 
						|
         return error4( 0, e4parm, E92508 ) ;
 | 
						|
      if ( (unsigned long)hashVal >= file->codeBase->opt.numLists )
 | 
						|
         return error4( 0, e4parm, E92508 ) ;
 | 
						|
   #endif
 | 
						|
   #ifdef E4ANALYZE
 | 
						|
      if ( block->changed != 0 )
 | 
						|
         return error4( file->codeBase, e4struct, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   l4add( &file->codeBase->opt.lists[hashVal], block ) ;
 | 
						|
   block->len = blockLen ;
 | 
						|
   block->pos = position ;
 | 
						|
   block->file = file ;
 | 
						|
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
int opt4blockClear( OPT4BLOCK *block )
 | 
						|
{
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( block == 0 )
 | 
						|
         return error4( 0, e4parm_null, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   block->changed = 0 ;
 | 
						|
   block->len = 0 ;
 | 
						|
   block->pos = 0 ;
 | 
						|
   block->file = 0 ;
 | 
						|
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef S4WRITE_DELAY
 | 
						|
void S4CALL opt4writeCompletionRoutine( void *inDelay )
 | 
						|
{
 | 
						|
   FILE4WRITE_DELAY *delay ;
 | 
						|
 | 
						|
   delay = (FILE4WRITE_DELAY *)inDelay ;
 | 
						|
   EnterCriticalSection( &delay->file->codeBase->opt.critical4optWrite ) ;
 | 
						|
   l4add( &delay->file->codeBase->opt.delayAvail, (LINK4 *)delay->completionData ) ;
 | 
						|
   LeaveCriticalSection( &delay->file->codeBase->opt.critical4optWrite ) ;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef _MSC_VER
 | 
						|
   #ifdef P4ARGS_USED
 | 
						|
   #pragma argsused
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
static int opt4blockFlush( OPT4 *opt, OPT4BLOCK *block, char buffer, int doDelay )
 | 
						|
{
 | 
						|
   int rc, flushBuffer ;
 | 
						|
   #ifdef S4WRITE_DELAY
 | 
						|
      OPT4BLOCK *delayBlock ;
 | 
						|
      LINK4 *delayLink ;
 | 
						|
   #endif
 | 
						|
   #ifdef __SC__   /* compiler bug - cpp */
 | 
						|
      unsigned long long_val ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( block == 0 )
 | 
						|
         return error4( 0, e4parm_null, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   #ifdef E4ANALYZE_ALL
 | 
						|
      if ( block->file->hasDup == 1 )
 | 
						|
         if ( file4cmpPart( block->file->codeBase, block->data, block->file, block->pos, block->len ) != 0 )
 | 
						|
            return error4( block->file->codeBase, e4opt, E82503 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   if ( opt->readFile == block->file )  /* check to see if block has been temporarily read-buffered */
 | 
						|
      if ( ( (unsigned long)block->pos < opt->readStartPos + opt->bufferSize ) && ( (unsigned long)block->pos >= opt->readStartPos ) )
 | 
						|
         memcpy( opt->readBuffer + block->pos - opt->readStartPos, block->data, block->len ) ;
 | 
						|
 | 
						|
   if ( buffer && block->file->type == OPT4DBF )
 | 
						|
   {
 | 
						|
      if ( opt->writeFile != block->file )
 | 
						|
         flushBuffer = 1 ;
 | 
						|
      else
 | 
						|
      {
 | 
						|
         if ( opt->writeBlockCount == opt->maxBlocks )
 | 
						|
            flushBuffer = 1 ;
 | 
						|
         else
 | 
						|
         {
 | 
						|
            if ( opt->writeCurPos != (unsigned long)block->pos )   /* not a consecutive write, so check out possibilities */
 | 
						|
            {
 | 
						|
               if ( opt->writeCurPos < (unsigned long)block->pos )
 | 
						|
               {
 | 
						|
                  if ( (unsigned long)block->pos - opt->writeCurPos < opt->blockSize )   /* partially filled block, can just extend */
 | 
						|
                  {
 | 
						|
                     opt->writeCurPos = (unsigned long)block->pos ;
 | 
						|
                     flushBuffer = 0 ;
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                     flushBuffer = 1 ;
 | 
						|
               }
 | 
						|
               else
 | 
						|
                  flushBuffer = 1 ;
 | 
						|
            }
 | 
						|
            else   /* just want to write at current spot, so go ahead */
 | 
						|
               flushBuffer = 0 ;
 | 
						|
         }
 | 
						|
 | 
						|
      }
 | 
						|
 | 
						|
      #ifdef E4ANALYZE
 | 
						|
         if ( flushBuffer < 0 || flushBuffer > 1 )
 | 
						|
            return error4( block->file->codeBase, e4info, E92508 ) ;
 | 
						|
      #endif
 | 
						|
 | 
						|
      if ( flushBuffer == 1 )
 | 
						|
      {
 | 
						|
         if ( ( rc = opt4flushWriteBuffer( opt ) ) != 0 )
 | 
						|
            return rc ;
 | 
						|
         opt->writeFile = block->file ;
 | 
						|
         opt->writeStartPos = opt->writeCurPos = (unsigned long)block->pos ;
 | 
						|
      }
 | 
						|
      memcpy( opt->writeBuffer + (opt->writeCurPos - opt->writeStartPos), block->data, block->len ) ;
 | 
						|
      opt->writeCurPos += block->len ;
 | 
						|
      opt->writeBlockCount++ ;
 | 
						|
 | 
						|
      #ifdef E4ANALYZE_ALL
 | 
						|
         if ( block->file->hasDup == 1 )
 | 
						|
            if ( file4cmpPart( opt->writeFile->codeBase, opt->writeBuffer, opt->writeFile, opt->writeStartPos, (unsigned)(opt->writeCurPos - opt->writeStartPos) ) != 0 )
 | 
						|
               return error4( block->file->codeBase, e4opt, E82503 ) ;
 | 
						|
      #endif
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      #ifdef __SC__   /* compiler bug - cpp */
 | 
						|
         if ( opt->writeFile == block->file )
 | 
						|
         {
 | 
						|
            long_val = opt->writeBlockCount ;
 | 
						|
            long_val *= opt->blockSize ;
 | 
						|
 | 
						|
            if ( opt->writeCurPos >= (unsigned long) block->pos &&
 | 
						|
                 (opt->writeCurPos - long_val ) <= (unsigned long)block->pos )
 | 
						|
               if ( ( rc = opt4flushWriteBuffer( opt ) ) != 0 )
 | 
						|
                  return rc ;
 | 
						|
         }
 | 
						|
      #else
 | 
						|
         if ( opt->writeFile == block->file )
 | 
						|
            if ( opt->writeCurPos >= (unsigned long) block->pos &&
 | 
						|
                 (opt->writeCurPos - opt->writeBlockCount * opt->blockSize ) <= (unsigned long)block->pos )
 | 
						|
               if ( ( rc = opt4flushWriteBuffer( opt ) ) != 0 )
 | 
						|
                  return rc ;
 | 
						|
      #endif
 | 
						|
      block->file->doBuffer = 0 ;
 | 
						|
      #ifdef S4WRITE_DELAY
 | 
						|
         if ( doDelay == 1 && opt->delayWriteBuffer != 0 )
 | 
						|
         {
 | 
						|
            for ( ;; )  /* wait until a block is available */
 | 
						|
            {
 | 
						|
               delayLink = (LINK4 *)l4first( &opt->delayAvail ) ;
 | 
						|
               if ( delayLink == 0 )
 | 
						|
                  Sleep( 0 ) ;
 | 
						|
               else
 | 
						|
                  break ;
 | 
						|
            }
 | 
						|
 | 
						|
            delayBlock = (OPT4BLOCK *)(delayLink - 1) ;
 | 
						|
            EnterCriticalSection( &opt->critical4optWrite ) ;
 | 
						|
            l4remove( &opt->delayAvail, delayLink ) ;
 | 
						|
            LeaveCriticalSection( &opt->critical4optWrite ) ;
 | 
						|
            memcpy( delayBlock->data, block->data, block->len ) ;
 | 
						|
 | 
						|
            file4writeOpt( block->file, block->pos, delayBlock->data, block->len, 1, opt4writeCompletionRoutine, &delayBlock->lruLink ) ;
 | 
						|
         }
 | 
						|
         else
 | 
						|
      #endif
 | 
						|
      {
 | 
						|
         if ( ( rc = file4writeOpt( block->file, block->pos, block->data, block->len, 0, 0, 0 ) ) != 0 )
 | 
						|
            return rc ;
 | 
						|
      }
 | 
						|
      block->file->doBuffer = 1 ;
 | 
						|
   }
 | 
						|
 | 
						|
   block->changed = 0 ;
 | 
						|
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
/* esp. in delay-write, may return a different block back to user for
 | 
						|
   use, instead of the current one */
 | 
						|
/*OPT4BLOCK *opt4blockRemove( OPT4 *opt, OPT4BLOCK *block, int doFlush )*/
 | 
						|
int opt4blockRemove( OPT4 *opt, OPT4BLOCK *block, int doFlush )
 | 
						|
{
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( block == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm_null, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   if ( doFlush && block->changed )
 | 
						|
   {
 | 
						|
      block->file->doBuffer = 0 ;
 | 
						|
      opt4blockFlush( opt, block, 1, 1 ) ;
 | 
						|
      block->file->doBuffer = 1 ;
 | 
						|
   }
 | 
						|
 | 
						|
   if ( block->file != 0 )
 | 
						|
   {
 | 
						|
      l4remove( &opt->lists[opt4fileHash( opt, block->file, (unsigned long)block->pos )], block ) ;
 | 
						|
      block->file = 0 ;
 | 
						|
   }
 | 
						|
 | 
						|
   opt4blockClear( block ) ;
 | 
						|
 | 
						|
/*   return block ;  */
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
/* function to remove, without saving, parts of an optimized file */
 | 
						|
int opt4fileDelete( FILE4 *file, long lowPos, long hiPos )
 | 
						|
{
 | 
						|
   long onPos, endDeletePos, hashVal ;
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   OPT4 *opt ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 || lowPos < 0 || hiPos < 0 )
 | 
						|
         return error4( 0, e4parm, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   opt = &file->codeBase->opt ;
 | 
						|
 | 
						|
   hashVal = opt4fileHash( opt, file, (unsigned long)lowPos ) ;
 | 
						|
   if ( hashVal != opt4fileHash( opt, file, lowPos + file->codeBase->opt.blockSize - 1L ) )  /* not on a block boundary, so delete partial */
 | 
						|
   {
 | 
						|
      /* lowPos / blockSize because position must be on a block boundary */
 | 
						|
      blockOn = opt4fileReturnBlock( file, lowPos / file->codeBase->opt.blockSize, hashVal ) ;
 | 
						|
      if ( blockOn )  /* block in memory, so partially delete */
 | 
						|
      {
 | 
						|
         if ( file->len <= hiPos )   /* just removing all file, so merely delete */
 | 
						|
            blockOn->len = (unsigned)(lowPos - blockOn->pos) ;
 | 
						|
         else  /* read the old data into the block - ignore errors since could be eof, else catch later */
 | 
						|
            file4read( file, lowPos, (char *)blockOn->data + blockOn->pos - lowPos, (unsigned)(opt->blockSize - (lowPos - blockOn->pos)) ) ;
 | 
						|
      }
 | 
						|
      onPos = ( (lowPos + opt->blockSize) >> opt->blockPower ) << opt->blockPower ;
 | 
						|
   }
 | 
						|
   else
 | 
						|
      onPos = lowPos ;
 | 
						|
 | 
						|
   endDeletePos = hiPos + opt->blockSize - 1 ;
 | 
						|
   while( onPos < endDeletePos )
 | 
						|
   {
 | 
						|
      /* onPos / blockSize because position must be on a block boundary */
 | 
						|
      blockOn = opt4fileReturnBlock( file, onPos / file->codeBase->opt.blockSize, opt4fileHash( opt, file, (unsigned long)onPos ) ) ;
 | 
						|
      if ( blockOn )  /* block in memory, so delete */
 | 
						|
      {
 | 
						|
         opt4blockRemove( opt, blockOn, 0 ) ;
 | 
						|
         opt4blockLruTop( blockOn ) ;
 | 
						|
         l4addBefore( &opt->avail, l4first( &opt->avail ), &blockOn->lruLink ) ;
 | 
						|
      }
 | 
						|
      onPos += opt->blockSize ;
 | 
						|
   }
 | 
						|
 | 
						|
   onPos -= opt->blockSize ;
 | 
						|
   if ( onPos < hiPos )
 | 
						|
   {
 | 
						|
      blockOn = opt4fileReturnBlock( file, (unsigned long)onPos, opt4fileHash( opt, file, (unsigned long)onPos ) ) ;
 | 
						|
      if ( blockOn )
 | 
						|
      {
 | 
						|
         if ( file->len <= hiPos )
 | 
						|
         {
 | 
						|
            opt4blockRemove( opt, blockOn, 0 ) ;
 | 
						|
            opt4blockLruTop( blockOn ) ;
 | 
						|
            l4addBefore( &opt->avail, l4first( &opt->avail ), &blockOn->lruLink ) ;
 | 
						|
         }
 | 
						|
         else
 | 
						|
            file4read( file, onPos, blockOn->data, (unsigned)(hiPos - blockOn->pos) ) ;
 | 
						|
      }
 | 
						|
   }
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
int opt4flushAll( OPT4 *opt, char doFree )
 | 
						|
{
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   LINK4 *linkOn, *nextLink ;
 | 
						|
   LIST4 *flushList ;
 | 
						|
   char i ;
 | 
						|
   int rc, saveRc ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( opt == 0 )
 | 
						|
         return error4( 0, e4parm_null, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   saveRc = opt4flushWriteBuffer( opt ) ;
 | 
						|
 | 
						|
   for ( i = 0 ; i < OPT4NUM_LISTS ; i++ )
 | 
						|
   {
 | 
						|
      flushList = &opt->prio[i]->list ;
 | 
						|
 | 
						|
      for( linkOn = (LINK4 *)l4first( flushList ) ; linkOn != 0; )
 | 
						|
      {
 | 
						|
         blockOn = (OPT4BLOCK *)( linkOn - 1 ) ;
 | 
						|
         if( blockOn->changed )
 | 
						|
         {
 | 
						|
            rc = opt4blockFlush( opt, blockOn, 1, 0 ) ;
 | 
						|
            if ( rc != 0 )
 | 
						|
               saveRc = rc ;
 | 
						|
         }
 | 
						|
 | 
						|
         if ( doFree )
 | 
						|
         {
 | 
						|
            nextLink = (LINK4 *)l4next( flushList, linkOn ) ;
 | 
						|
            l4remove( &opt->lists[ opt4fileHash( opt, blockOn->file, (unsigned long)blockOn->pos )], blockOn ) ;
 | 
						|
            opt4blockLruTop( blockOn ) ;
 | 
						|
            l4add( &opt->avail, linkOn ) ;
 | 
						|
            linkOn = nextLink ;
 | 
						|
            opt4blockClear( blockOn ) ;
 | 
						|
         }
 | 
						|
         else
 | 
						|
            linkOn = (LINK4 *)l4next( flushList, linkOn ) ;
 | 
						|
      }
 | 
						|
   }
 | 
						|
   return saveRc ;
 | 
						|
}
 | 
						|
 | 
						|
int opt4fileFlushList( OPT4 *opt, FILE4 *file, LIST4 *flushList, int doFree )
 | 
						|
{
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   LINK4 *linkOn, *nextLink ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 || flushList == 0 )
 | 
						|
         return error4( 0, e4parm, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   for( linkOn = (LINK4 *)l4first( flushList ) ; linkOn != 0; )
 | 
						|
   {
 | 
						|
      blockOn = (OPT4BLOCK *)( linkOn - 1 ) ;
 | 
						|
      if( blockOn->file == file )
 | 
						|
      {
 | 
						|
         if( blockOn->changed )
 | 
						|
            if ( opt4blockFlush( opt, blockOn, 1, 0 ) < 0 )
 | 
						|
               return -1 ;
 | 
						|
 | 
						|
         if ( doFree )
 | 
						|
         {
 | 
						|
            nextLink = (LINK4 *)l4next( flushList, linkOn ) ;
 | 
						|
            l4remove( &opt->lists[ opt4fileHash( opt, file, (unsigned long)blockOn->pos )], blockOn ) ;
 | 
						|
            opt4blockLruTop( blockOn ) ;
 | 
						|
            l4add( &opt->avail, linkOn ) ;
 | 
						|
            linkOn = nextLink ;
 | 
						|
            opt4blockClear( blockOn ) ;
 | 
						|
         }
 | 
						|
         else
 | 
						|
            linkOn = (LINK4 *)l4next( flushList, linkOn ) ;
 | 
						|
      }
 | 
						|
      else
 | 
						|
         linkOn = (LINK4 *)l4next( flushList, linkOn ) ;
 | 
						|
   }
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef S4WRITE_DELAY
 | 
						|
void S4CALL opt4largeWriteCompletionRoutine( void *delay )
 | 
						|
{
 | 
						|
   /* just resets the avail flag */
 | 
						|
   *((int *)(((FILE4WRITE_DELAY *)(delay))->completionData)) = 1 ;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
int opt4flushWriteBuffer( OPT4 *opt )
 | 
						|
{
 | 
						|
   int rc, oldDoBuffer, oldBufferWrites ;
 | 
						|
   FILE4 *file ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( opt == 0 )
 | 
						|
         return error4( 0, e4parm_null, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   if ( opt->writeBlockCount != 0 )
 | 
						|
   {
 | 
						|
      file = opt->writeFile ;
 | 
						|
      #ifdef E4ANALYZE_ALL
 | 
						|
         if ( file->hasDup == 1 )
 | 
						|
            if ( file4cmpPart( file->codeBase, opt->writeBuffer, file, opt->writeStartPos, (unsigned)(opt->writeCurPos - opt->writeStartPos) ) != 0 )
 | 
						|
               return error4( file->codeBase, e4opt, E80602 ) ;
 | 
						|
      #endif
 | 
						|
 | 
						|
      oldDoBuffer = file->doBuffer ;
 | 
						|
      oldBufferWrites = file->bufferWrites ;
 | 
						|
      file->doBuffer = 0 ;
 | 
						|
      file->bufferWrites = 0 ;
 | 
						|
 | 
						|
      #ifdef S4WRITE_DELAY
 | 
						|
         if ( opt->writeBuffer == opt->writeBufferActual )
 | 
						|
         {
 | 
						|
            opt->writeBufferActualAvail = 0 ;
 | 
						|
            rc = file4writeDelay( file, opt->writeStartPos, opt->writeBuffer,
 | 
						|
               (unsigned)(opt->writeCurPos - opt->writeStartPos), opt4largeWriteCompletionRoutine, &opt->writeBufferActualAvail ) ;
 | 
						|
            while ( opt->delayLargeBufferAvail != 1 )  /* wait so we can use this buffer */
 | 
						|
               Sleep( 0 ) ;
 | 
						|
            opt->writeBuffer = opt->delayLargeBuffer ;
 | 
						|
         }
 | 
						|
         else
 | 
						|
         {
 | 
						|
            #ifdef E4ANALYZE
 | 
						|
               if ( opt->writeBuffer != opt->delayLargeBuffer )
 | 
						|
                  return error4( 0, e4struct, E92508 ) ;
 | 
						|
            #endif
 | 
						|
            opt->delayLargeBufferAvail = 0 ;
 | 
						|
            rc = file4writeDelay( file, opt->writeStartPos, opt->writeBuffer, (unsigned)(opt->writeCurPos - opt->writeStartPos), opt4largeWriteCompletionRoutine, &opt->delayLargeBufferAvail ) ;
 | 
						|
            while ( opt->writeBufferActualAvail != 1 )  /* wait so we can use this buffer */
 | 
						|
               Sleep( 0 ) ;
 | 
						|
            opt->writeBuffer = opt->writeBufferActual ;
 | 
						|
         }
 | 
						|
      #else
 | 
						|
         rc = file4write( file, opt->writeStartPos, opt->writeBuffer, (unsigned)(opt->writeCurPos - opt->writeStartPos) ) ;
 | 
						|
      #endif
 | 
						|
      file->doBuffer = oldDoBuffer ;
 | 
						|
      file->bufferWrites = oldBufferWrites ;
 | 
						|
      if ( rc != 0 )
 | 
						|
         return rc ;
 | 
						|
      opt->writeStartPos = opt->writeCurPos = opt->writeBlockCount = 0 ;
 | 
						|
   }
 | 
						|
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
/* this function sets the 'selected' member of the lru-list to show which
 | 
						|
   links are considered available. */
 | 
						|
static void opt4fileMarkAvailable( OPT4 *opt, int useCount )
 | 
						|
{
 | 
						|
   OPT4LIST *optList ;
 | 
						|
   OPT4BLOCK *block ;
 | 
						|
   LINK4 *link ;
 | 
						|
   LIST4 *list ;
 | 
						|
   int i ;
 | 
						|
   unsigned short int linksLeft ;
 | 
						|
   unsigned long elapsedTime ;
 | 
						|
 | 
						|
   for ( i = 0 ; i < OPT4NUM_LISTS ; i++ )
 | 
						|
   {
 | 
						|
      optList = opt->prio[i] ;
 | 
						|
 | 
						|
      if ( useCount )
 | 
						|
      {
 | 
						|
         optList->currentPrioCount-- ;
 | 
						|
         if ( optList->currentPrioCount > 0 )
 | 
						|
            continue ;
 | 
						|
         optList->currentPrioCount = i ;  /* reset priority */
 | 
						|
      }
 | 
						|
 | 
						|
      list = &optList->list ;
 | 
						|
 | 
						|
      for ( linksLeft = l4numNodes( list ), link = (LINK4 *)l4first( list ) ;; linksLeft--, link = (LINK4 *)l4next( list, link ) )
 | 
						|
      {
 | 
						|
         if ( link == 0 )
 | 
						|
            break ;
 | 
						|
         block = (OPT4BLOCK *)(link - 1) ;
 | 
						|
 | 
						|
         elapsedTime = opt->accessTimeCount - block->accessTime ;
 | 
						|
         if ( elapsedTime < optList->minTime )  /* all blocks this new must remain on list */
 | 
						|
            break ;
 | 
						|
 | 
						|
         /* other wise determine based on minimum links */
 | 
						|
 | 
						|
         elapsedTime = opt->readTimeCount - block->readTime ;
 | 
						|
         if ( elapsedTime > optList->maxTime )  /* candidate for removal */
 | 
						|
         {
 | 
						|
            list->selected = link ;
 | 
						|
            continue ;
 | 
						|
         }
 | 
						|
 | 
						|
         /* else use maximum link information */
 | 
						|
         if ( linksLeft > optList->minLink )
 | 
						|
         {
 | 
						|
            list->selected = link ;
 | 
						|
            continue ;
 | 
						|
         }
 | 
						|
 | 
						|
         /* else at or below the minimum allowed links, so stop */
 | 
						|
         break ;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( list->selected == 0 )   /* there were none available, so increase the time before a re-scan */
 | 
						|
         optList->currentPrioCount++ ;
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
/* also sets the priority scheme */
 | 
						|
static OPT4BLOCK *opt4fileChooseBlock( FILE4 *file )
 | 
						|
{
 | 
						|
   LINK4 *lruLink ;
 | 
						|
   LIST4 *chooseList ;
 | 
						|
   OPT4 *opt ;
 | 
						|
   int i, listAvg, l1, l2, l3 ;
 | 
						|
   OPT4BLOCK *block ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm_null, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   opt = &file->codeBase->opt ;
 | 
						|
 | 
						|
   if ( opt->avail.nLink )
 | 
						|
      chooseList = &opt->avail ;
 | 
						|
   else
 | 
						|
   {
 | 
						|
      for( i = 0 ; i < OPT4NUM_LISTS ; i++ )
 | 
						|
      {
 | 
						|
         chooseList = &opt->prio[i]->list ;
 | 
						|
         if ( chooseList->selected != 0 )   /* we have the desired link */
 | 
						|
            break ;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( i >= OPT4NUM_LISTS )
 | 
						|
      {
 | 
						|
         /* no candidate lists were found, so try finding something available */
 | 
						|
         opt4fileMarkAvailable( opt, 1 ) ;
 | 
						|
 | 
						|
         for( i = 0 ; i < OPT4NUM_LISTS ; i++ )
 | 
						|
         {
 | 
						|
            chooseList = &opt->prio[i]->list ;
 | 
						|
            if ( chooseList->selected != 0 )   /* we have the desired link */
 | 
						|
               break ;
 | 
						|
         }
 | 
						|
 | 
						|
         if ( i >= OPT4NUM_LISTS )
 | 
						|
         {
 | 
						|
            /* no candidate lists were found, mark available for ALL lists */
 | 
						|
            opt4fileMarkAvailable( opt, 0 ) ;
 | 
						|
 | 
						|
            for( i = 0 ; i < OPT4NUM_LISTS ; i++ )
 | 
						|
            {
 | 
						|
               chooseList = &opt->prio[i]->list ;
 | 
						|
               if ( chooseList->selected != 0 )   /* we have the desired link */
 | 
						|
                  break ;
 | 
						|
            }
 | 
						|
 | 
						|
            if ( i >= OPT4NUM_LISTS )
 | 
						|
            {
 | 
						|
               /* no candidate lists were found, so try list normalization */
 | 
						|
               l1 = l4numNodes( &opt->prio[0]->list ) ;
 | 
						|
               l2 = l4numNodes( &opt->prio[1]->list ) ;
 | 
						|
               l3 = l4numNodes( &opt->prio[2]->list ) ;
 | 
						|
               listAvg = (l1 + l2 + l3) / 3  ;
 | 
						|
 | 
						|
               chooseList = 0 ;
 | 
						|
 | 
						|
               if ( listAvg > 0 )
 | 
						|
               {
 | 
						|
                  if ( l1 > listAvg )
 | 
						|
                     chooseList = &opt->prio[0]->list ;
 | 
						|
                  else
 | 
						|
                  {
 | 
						|
                     if ( l2 > listAvg )
 | 
						|
                        chooseList = &opt->prio[1]->list ;
 | 
						|
                     else
 | 
						|
                        chooseList = &opt->prio[2]->list ;
 | 
						|
                  }
 | 
						|
               }
 | 
						|
               else
 | 
						|
               {
 | 
						|
                  /* just try to get a link */
 | 
						|
                  chooseList = &opt->prio[3]->list ;
 | 
						|
                  if ( l4numNodes( chooseList ) == 0 )
 | 
						|
                  {
 | 
						|
                     chooseList = &opt->prio[4]->list ;
 | 
						|
                     if ( l4numNodes( chooseList ) == 0 )
 | 
						|
                        chooseList = &opt->prio[5]->list ;
 | 
						|
                  }
 | 
						|
 | 
						|
               }
 | 
						|
            }
 | 
						|
         }
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   lruLink = (LINK4 *)l4first( chooseList ) ;
 | 
						|
   if ( lruLink == 0 )   /* none were available, this is an error */
 | 
						|
   {
 | 
						|
      error4( 0, e4struct, E92508 ) ;
 | 
						|
      return 0 ;
 | 
						|
   }
 | 
						|
   if ( lruLink == chooseList->selected )  /* lru link is selected, set selected to null */
 | 
						|
      chooseList->selected = (LINK4 *)0 ;
 | 
						|
   l4remove( chooseList, lruLink ) ;
 | 
						|
   block = (OPT4BLOCK *)( lruLink - 1 ) ;
 | 
						|
   return block ;
 | 
						|
}
 | 
						|
 | 
						|
static void opt4blockUpgradePriorityCheck( OPT4BLOCK *block, OPT4 *opt )
 | 
						|
{
 | 
						|
   #ifndef S4OFF_INDEX
 | 
						|
      #ifdef N4OTHER
 | 
						|
         TAG4FILE *t4file ;
 | 
						|
      #else
 | 
						|
         INDEX4FILE *i4file ;
 | 
						|
      #endif
 | 
						|
   #endif
 | 
						|
   DATA4FILE *d4file ;
 | 
						|
 | 
						|
   if ( block->optList == &opt->dbfLo )  /* maybe move to hi */
 | 
						|
   {
 | 
						|
      d4file = ((DATA4FILE *)block->file->ownerPtr) ;
 | 
						|
      /* NOT moved to hi-priority if record size > 4k, to avoid memory congestion (except header pos 0 block) */
 | 
						|
      if ( d4file != 0 )
 | 
						|
         if ( block->pos == 0 || (d4file->hiPrio == 1 && dfile4recWidth( d4file ) > 4096 ) )
 | 
						|
         {
 | 
						|
            l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
            opt4listLruBottomPlace( &opt->dbfHi, block ) ;
 | 
						|
         }
 | 
						|
      return ;
 | 
						|
   }
 | 
						|
 | 
						|
   #ifndef S4OFF_INDEX
 | 
						|
      if ( block->optList == &opt->indexLo )  /* maybe move to hi */
 | 
						|
      {
 | 
						|
         #ifdef N4OTHER
 | 
						|
            t4file = ((TAG4FILE *)block->file->ownerPtr) ;
 | 
						|
 | 
						|
            if ( t4file != 0 )
 | 
						|
            {
 | 
						|
               if ( t4file->readBlockTag == 0 || block->len != opt->blockSize )  /* not reading a block, so leave at low priority */
 | 
						|
                  return ;
 | 
						|
 | 
						|
               /* first ensure that it is on a real index boundary, and that we read the whole block */
 | 
						|
               if ( opt->blockSize / 2 == B4BLOCK_SIZE )  /* must check both blocks */
 | 
						|
               {
 | 
						|
                  if ( b4dataLeaf( block->data, t4file->readBlockTag ) == 0 )
 | 
						|
                  {
 | 
						|
                     l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                     opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                     return ;
 | 
						|
                  }
 | 
						|
                  if ( b4dataLeaf( (char *)block->data + B4BLOCK_SIZE, t4file->readBlockTag ) == 0 )
 | 
						|
                  {
 | 
						|
                     l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                     opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                  }
 | 
						|
               }
 | 
						|
 | 
						|
               if ( opt->blockSize == B4BLOCK_SIZE )
 | 
						|
                  if ( b4dataLeaf( block->data, t4file->readBlockTag ) == 0 )
 | 
						|
                  {
 | 
						|
                     l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                     opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                  }
 | 
						|
            }
 | 
						|
         #else
 | 
						|
            i4file = ((INDEX4FILE *)block->file->ownerPtr) ;
 | 
						|
 | 
						|
            if ( i4file != 0 )
 | 
						|
            {
 | 
						|
               if ( i4file->readBlockTag == 0 || block->len != opt->blockSize )  /* not reading a block, so leave at low priority */
 | 
						|
                  return ;
 | 
						|
 | 
						|
               /* first ensure that it is on a real index boundary, and that we read the whole block */
 | 
						|
               #ifdef S4MDX
 | 
						|
                  if ( (unsigned long)i4file->header.blockRw != opt->blockSize )
 | 
						|
                     return ;
 | 
						|
 | 
						|
                  /* if it is a branch block, then upgrade it's priority */
 | 
						|
                  if ( b4dataLeaf( block->data, i4file->readBlockTag ) == 0 )
 | 
						|
                  {
 | 
						|
                     l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                     opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                  }
 | 
						|
               #else
 | 
						|
                  if ( opt->blockSize / 2 == B4BLOCK_SIZE )  /* must check both blocks */
 | 
						|
                  {
 | 
						|
                     if ( b4dataLeaf( block->data, i4file->readBlockTag ) == 0 )
 | 
						|
                     {
 | 
						|
                        l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                        opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                        return ;
 | 
						|
                     }
 | 
						|
                     if ( b4dataLeaf( (char *)block->data + B4BLOCK_SIZE, i4file->readBlockTag ) == 0 )
 | 
						|
                     {
 | 
						|
                        l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                        opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                     }
 | 
						|
                  }
 | 
						|
 | 
						|
                  if ( opt->blockSize == B4BLOCK_SIZE )
 | 
						|
                     if ( b4dataLeaf( block->data, i4file->readBlockTag ) == 0 )
 | 
						|
                     {
 | 
						|
                        l4remove( &block->optList->list, &block->lruLink ) ;
 | 
						|
                        opt4listLruBottomPlace( &opt->indexHi, block ) ;
 | 
						|
                     }
 | 
						|
               #endif
 | 
						|
            }
 | 
						|
         #endif
 | 
						|
      }
 | 
						|
   #endif /* S4OFF_INDEX */
 | 
						|
}
 | 
						|
 | 
						|
static OPT4LIST *opt4listDetermine( OPT4 *opt, FILE4 *file, int hiPrio )
 | 
						|
{
 | 
						|
   switch ( file->type )
 | 
						|
   {
 | 
						|
      case OPT4DBF:
 | 
						|
         if ( hiPrio == 1 )
 | 
						|
            return &opt->dbfHi ;
 | 
						|
         else
 | 
						|
            return &opt->dbfLo ;
 | 
						|
      case OPT4INDEX:
 | 
						|
         if ( hiPrio == 1 )
 | 
						|
            return &opt->indexHi ;
 | 
						|
         else
 | 
						|
            return &opt->indexLo ;
 | 
						|
      default:
 | 
						|
         return &opt->other ;
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
/* gets an available block, and places it on the appropriate lru-list */
 | 
						|
/* also sets the readTime and accessTime for the block */
 | 
						|
static OPT4BLOCK *opt4fileGetBlock( OPT4 *opt, FILE4 *file, int hiPrio )
 | 
						|
{
 | 
						|
   OPT4BLOCK *block ;
 | 
						|
   OPT4LIST *optList ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm_null, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
 | 
						|
   #endif
 | 
						|
 | 
						|
   block = opt4fileChooseBlock( file ) ;
 | 
						|
   opt4blockRemove( opt, block, 1 ) ;
 | 
						|
 | 
						|
   optList = opt4listDetermine( opt, file, hiPrio ) ;
 | 
						|
   opt4listLruBottomPlace( optList, block ) ;
 | 
						|
 | 
						|
   block->readTime = opt->readTimeCount++ ;
 | 
						|
   if ( opt->readTimeCount == 0 )  /* time count got reset, so reset for everyone */
 | 
						|
      opt4timeReset( opt, 1, 0 ) ;
 | 
						|
   block->accessTime = ++opt->accessTimeCount ;
 | 
						|
 | 
						|
   return block ;
 | 
						|
}
 | 
						|
 | 
						|
static void opt4timeReset( OPT4 *opt, int doReadTime, int doAccessTime )
 | 
						|
{
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   LIST4 *listOn ;
 | 
						|
   int i ;
 | 
						|
 | 
						|
   /* for simplicity, just reset all blocks read-time to 0 (i.e. just read) */
 | 
						|
   /* in general this function will rarely be called, thus the impact should be minor */
 | 
						|
 | 
						|
   for ( i = 0 ; i < OPT4NUM_LISTS ; i++ )
 | 
						|
   {
 | 
						|
      listOn = &opt->prio[i]->list ;
 | 
						|
      blockOn = (OPT4BLOCK *)l4first( listOn ) ;
 | 
						|
      for( ;; )
 | 
						|
      {
 | 
						|
         if ( blockOn == 0 )
 | 
						|
            break ;
 | 
						|
         if ( doReadTime )
 | 
						|
            blockOn->readTime = 0 ;
 | 
						|
         if ( doAccessTime )
 | 
						|
            blockOn->accessTime = 0 ;
 | 
						|
         blockOn = (OPT4BLOCK *)l4next( listOn, blockOn ) ;
 | 
						|
      }
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
/* inlined now
 | 
						|
long opt4fileHash( OPT4 *opt, FILE4 *file, unsigned long pos )
 | 
						|
{
 | 
						|
   return ( (( file->hashInit + pos ) >> opt->blockPower ) & opt->mask ) ;
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
int opt4fileWrite( FILE4 *file, long pos, unsigned len, const void *data, char changed )
 | 
						|
{
 | 
						|
   long hashVal, adjustedPos, lenWritten ;
 | 
						|
   unsigned readLen, lenRead, extraRead ;
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   OPT4 *opt ;
 | 
						|
   int doUpgradeCheck ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 || pos < 0 || data == 0 )
 | 
						|
         return error4( 0, e4parm, E92508 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   #ifdef S4ADVANCE_READ
 | 
						|
      /* ensure that the information to be written replaces any advanced-read
 | 
						|
         information in order to ensure that the advance-read data is up to
 | 
						|
         date */
 | 
						|
 | 
						|
      if ( changed == 1 )
 | 
						|
         file4advanceReadWriteOver( file, pos, len, data, 1 ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   opt = &file->codeBase->opt ;
 | 
						|
   lenWritten = 0 ;
 | 
						|
   extraRead = (unsigned) ((unsigned long)((unsigned long)pos << opt->numShift ) >> opt->numShift ) ;
 | 
						|
   adjustedPos = pos - extraRead ;
 | 
						|
   if( (long)len > (long)( (long)opt->numBlocks * (long)opt->blockSize ) )
 | 
						|
   {
 | 
						|
      /* case where amount to write > total bufferred length - do a piece at a time */
 | 
						|
      adjustedPos = (long)( ((long)opt->numBlocks - 1) * (long)opt->blockSize ) ;
 | 
						|
      for ( lenWritten = 0L ; (long)len > lenWritten ; lenWritten += adjustedPos )
 | 
						|
      {
 | 
						|
         if ( ( (long)len - lenWritten ) < adjustedPos )
 | 
						|
            adjustedPos = len - lenWritten ;
 | 
						|
         if ( opt4fileWrite( file, pos + lenWritten, (unsigned)adjustedPos, (char *)data + lenWritten, changed ) != adjustedPos )
 | 
						|
            return (int)lenWritten ;
 | 
						|
      }
 | 
						|
      return (int)lenWritten ;
 | 
						|
   }
 | 
						|
   len += extraRead ;
 | 
						|
 | 
						|
   do
 | 
						|
   {
 | 
						|
      hashVal = opt4fileHash( opt, file, (unsigned long)adjustedPos ) ;
 | 
						|
      readLen = (unsigned) ((len / (unsigned)opt->blockSize) ? opt->blockSize : len) ;
 | 
						|
      blockOn = opt4fileReturnBlock( file, adjustedPos, hashVal ) ;
 | 
						|
      if ( blockOn == 0 )
 | 
						|
      {
 | 
						|
         blockOn = opt4fileGetBlock( opt, file, 0 ) ;
 | 
						|
         #ifdef E4ANALYZE
 | 
						|
            if ( blockOn == 0 )
 | 
						|
               return error4( 0, e4info, E92508 ) ;
 | 
						|
         #endif
 | 
						|
         if ( (unsigned long)( readLen - extraRead ) < opt->blockSize && adjustedPos < file4len( file ) )
 | 
						|
            lenRead = opt4fileReadFile( file, adjustedPos, (char *)blockOn->data ) ;
 | 
						|
         else
 | 
						|
         {
 | 
						|
            if ( file4len( file ) >= (long) (pos + lenWritten + opt->blockSize) )   /* mark block as full to avoid file len set below... */
 | 
						|
               lenRead = (unsigned)opt->blockSize ;
 | 
						|
            else
 | 
						|
               lenRead = 0 ;
 | 
						|
         }
 | 
						|
         opt4blockAdd( blockOn, file, lenRead, hashVal, adjustedPos ) ;
 | 
						|
         doUpgradeCheck = 1 ;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( readLen < (unsigned) extraRead )
 | 
						|
         readLen = (unsigned) extraRead ;
 | 
						|
      memcpy( (char *)blockOn->data + extraRead, (char *)data + lenWritten, (unsigned)(readLen - extraRead) ) ;
 | 
						|
      if ( doUpgradeCheck == 1 )
 | 
						|
         opt4blockUpgradePriorityCheck( blockOn, opt ) ;
 | 
						|
 | 
						|
      opt4listLruBottomShift( blockOn ) ;
 | 
						|
      blockOn->changed |= changed ;
 | 
						|
      len -= readLen ;
 | 
						|
      lenWritten += readLen - extraRead ;
 | 
						|
      extraRead = 0 ;
 | 
						|
      adjustedPos += opt->blockSize ;
 | 
						|
      if ( pos + lenWritten > blockOn->pos + (long)blockOn->len )
 | 
						|
      {
 | 
						|
         if (file->len < ( pos + lenWritten ) ) /* file size has grown */
 | 
						|
         {
 | 
						|
            if ( blockOn->pos + (long)blockOn->len < pos )
 | 
						|
               memset( ((char *)blockOn->data) + blockOn->len, 0,(unsigned)( pos - ( blockOn->pos + blockOn->len ) ) ) ;
 | 
						|
            blockOn->len = (unsigned)( pos - blockOn->pos + lenWritten ) ;
 | 
						|
            if ( file->bufferWrites == 1 )
 | 
						|
            {
 | 
						|
               file->len = pos + lenWritten ;
 | 
						|
               #ifdef E4ANALYZE
 | 
						|
                  /* make sure the file length really has grown */
 | 
						|
                  if ( file->fileCreated )
 | 
						|
                     if ( u4filelength( file->hand ) > file->len )
 | 
						|
                        return error4( file->codeBase, e4info, E92508 ) ;
 | 
						|
               #endif
 | 
						|
            }
 | 
						|
         }
 | 
						|
         else   /* we have written past our end of block, but not our block, update our block len, dummy info */
 | 
						|
            blockOn->len = (unsigned) opt->blockSize ;
 | 
						|
      }
 | 
						|
   } while( len && blockOn->len == (unsigned)opt->blockSize ) ;
 | 
						|
 | 
						|
   return (unsigned) lenWritten ;
 | 
						|
}
 | 
						|
 | 
						|
unsigned opt4fileRead( FILE4 *f4, long pos, void *data, unsigned len )
 | 
						|
{
 | 
						|
   long hashVal, adjustedPos, lenRead ;
 | 
						|
   unsigned readLen, extraRead ;
 | 
						|
   int blocksNeeded ;
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   OPT4 *opt ;
 | 
						|
   DATA4FILE *d4file ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( f4 == 0 || pos < 0 || data == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   opt = &f4->codeBase->opt ;
 | 
						|
   lenRead = 0 ;
 | 
						|
   extraRead = (unsigned)((unsigned long)((unsigned long)pos << opt->numShift ) >> opt->numShift ) ;
 | 
						|
   adjustedPos = pos - extraRead ;
 | 
						|
   if( (unsigned long)len > ( opt->numBlocks * opt->blockSize ))
 | 
						|
   {
 | 
						|
      /* case where amount to read > total bufferred length - do a piece at a time */
 | 
						|
      adjustedPos = (long)( ((long)opt->numBlocks - 1) * (long)opt->blockSize ) ;
 | 
						|
      for ( lenRead = 0 ; (long)len > lenRead ; lenRead += adjustedPos )
 | 
						|
      {
 | 
						|
         if ( ( (long)len - lenRead ) < adjustedPos )
 | 
						|
            adjustedPos = len - lenRead ;
 | 
						|
         if ( opt4fileRead( f4, pos + lenRead, (char *)data + lenRead, (unsigned)adjustedPos ) != (unsigned)adjustedPos )
 | 
						|
            return (unsigned)lenRead ;
 | 
						|
      }
 | 
						|
      #ifdef E4ANALYZE_ALL
 | 
						|
         if ( f4->hasDup == 1 )
 | 
						|
            if ( file4cmpPart( f4->codeBase, data, f4, pos, (unsigned)lenRead ) != 0 )
 | 
						|
            {
 | 
						|
               error4( f4->codeBase, e4opt, E80602 ) ;
 | 
						|
               return 0 ;
 | 
						|
            }
 | 
						|
      #endif
 | 
						|
 | 
						|
      return (unsigned)lenRead ;
 | 
						|
   }
 | 
						|
   len += extraRead ;
 | 
						|
 | 
						|
   do
 | 
						|
   {
 | 
						|
      hashVal = opt4fileHash( opt, f4, (unsigned long)adjustedPos ) ;
 | 
						|
      readLen = (unsigned) ((len / opt->blockSize) ? opt->blockSize : len ) ;
 | 
						|
      blockOn = opt4fileReturnBlock( f4, adjustedPos, hashVal ) ;
 | 
						|
      if ( blockOn == 0 )  /* read from disk */
 | 
						|
      {
 | 
						|
         /* first ensure that the file is not a temporary file that is empty */
 | 
						|
         if ( f4->fileCreated == 0 )  /* not created, so if not in memory then empty */
 | 
						|
            return (unsigned)0 ;
 | 
						|
 | 
						|
         /* if force current is on, then pre-reading will be wasted, so just skip */
 | 
						|
         if ( opt->forceCurrent != 1 )
 | 
						|
         {
 | 
						|
            if ( f4->type == OPT4DBF )
 | 
						|
            {
 | 
						|
               d4file = (DATA4FILE *)f4->ownerPtr ;
 | 
						|
               if ( d4file != 0 )
 | 
						|
                  if ( d4file->hiPrio == -1 )
 | 
						|
                  {
 | 
						|
                     opt4fileReadSpBuffer( f4, (unsigned long)adjustedPos, -1, -1 ) ;
 | 
						|
                     blockOn = opt4fileReturnBlock( f4, adjustedPos, hashVal ) ;
 | 
						|
                  }
 | 
						|
            }
 | 
						|
 | 
						|
            if ( blockOn == 0 )
 | 
						|
            {
 | 
						|
               /* depending on the amount to read, it may still make sense to
 | 
						|
                  do advance reading -- quicker to read 2 blocks now than to
 | 
						|
                  read 1 block twice */
 | 
						|
               if ( len - readLen > 0 )  /* need to read more than 1 block */
 | 
						|
               {
 | 
						|
                  /* if the 2nd block is already in memory, don't bother */
 | 
						|
                  blockOn = opt4fileReturnBlock( f4, adjustedPos + opt->blockSize, opt4fileHash( opt, f4, (unsigned long)adjustedPos + opt->blockSize ) ) ;
 | 
						|
                  if ( blockOn == 0 )  /* not there, so get */
 | 
						|
                  {
 | 
						|
                     /* check to see how many blocks we need */
 | 
						|
                     blocksNeeded = 1 + len / readLen ;  /* 1st block, plus any extras */
 | 
						|
                     opt4fileReadSpBuffer( f4, (unsigned long)adjustedPos, blocksNeeded, 1 ) ;
 | 
						|
                     blockOn = opt4fileReturnBlock( f4, adjustedPos, hashVal ) ;
 | 
						|
                  }
 | 
						|
                  else  /* the 2nd block is there, so reset blockOn and read the first one */
 | 
						|
                     blockOn = 0 ;
 | 
						|
               }
 | 
						|
            }
 | 
						|
         }
 | 
						|
 | 
						|
         if ( blockOn == 0 )
 | 
						|
         {
 | 
						|
            blockOn = opt4fileGetBlock( opt, f4, 0 ) ;
 | 
						|
            opt4blockAdd( blockOn, f4, opt4fileReadFile( f4, adjustedPos, (char *)blockOn->data ), hashVal, adjustedPos ) ;
 | 
						|
            opt4blockUpgradePriorityCheck( blockOn, opt ) ;
 | 
						|
         }
 | 
						|
      }
 | 
						|
      else
 | 
						|
         if ( opt->forceCurrent == 1 )
 | 
						|
         {
 | 
						|
            if ( blockOn->changed == 0 && f4->bufferWrites == 0
 | 
						|
               #ifndef S4OFF_MULTI
 | 
						|
                  && f4->lowAccessMode == OPEN4DENY_NONE
 | 
						|
               #endif
 | 
						|
               )
 | 
						|
               opt4fileReadFile( f4, adjustedPos, (char *)blockOn->data ) ;
 | 
						|
         }
 | 
						|
 | 
						|
      opt4listLruBottomShift( blockOn ) ;
 | 
						|
 | 
						|
      if ( blockOn->len < readLen )
 | 
						|
         readLen = blockOn->len ;
 | 
						|
      if ( readLen < (unsigned) extraRead )
 | 
						|
         readLen = (unsigned) extraRead ;
 | 
						|
      memcpy( (char *)data + lenRead, (char *)blockOn->data + extraRead, (unsigned)(readLen - extraRead) ) ;
 | 
						|
      len -= readLen ;
 | 
						|
      lenRead += readLen - extraRead ;
 | 
						|
      extraRead = 0 ;
 | 
						|
      adjustedPos += opt->blockSize ;
 | 
						|
   } while( len && blockOn->len == (unsigned) opt->blockSize ) ;
 | 
						|
   #ifdef E4ANALYZE_ALL
 | 
						|
      if ( f4->hasDup == 1 )
 | 
						|
         if ( file4cmpPart( f4->codeBase, data, f4, pos, (unsigned)lenRead ) != 0 )
 | 
						|
         {
 | 
						|
            error4( f4->codeBase, e4opt, E80602 ) ;
 | 
						|
            return 0 ;
 | 
						|
         }
 | 
						|
   #endif
 | 
						|
 | 
						|
   return (unsigned)lenRead ;
 | 
						|
}
 | 
						|
 | 
						|
void opt4blockLruTop( OPT4BLOCK *block )
 | 
						|
{
 | 
						|
   LIST4 *list ;
 | 
						|
   LINK4 *l4link ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( block == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm_null, E92508 ) ;
 | 
						|
         return ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   list = &block->optList->list ;
 | 
						|
   l4link = &block->lruLink ;
 | 
						|
 | 
						|
   if ( list->selected == l4link )
 | 
						|
      list->selected = (LINK4 *)l4prev( list, l4link ) ;
 | 
						|
 | 
						|
   l4remove( list, l4link ) ;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef S4ADVANCE_TEST
 | 
						|
   unsigned S4FUNCTION opt4fileReadFile( FILE4 *file, const long posIn, char *buf )
 | 
						|
#else
 | 
						|
   static unsigned opt4fileReadFile( FILE4 *file, const long posIn, char *buf )
 | 
						|
#endif
 | 
						|
{
 | 
						|
   unsigned int len ;
 | 
						|
   OPT4 *opt ;
 | 
						|
   long pos ;
 | 
						|
   #ifdef S4ADVANCE_READ
 | 
						|
      unsigned int compareLen, copyOffset ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 || posIn < 0 || buf == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   pos = posIn ;
 | 
						|
   opt = &file->codeBase->opt ;
 | 
						|
 | 
						|
   #ifdef S4ADVANCE_READ
 | 
						|
      /* first check the advance-read buffer.  If the data is available there,
 | 
						|
         then copy it out.  Can't do more than that here because this function
 | 
						|
         is reading into a block already (don't want to overwrite that one by
 | 
						|
         mistake */
 | 
						|
 | 
						|
      /* only copy if can get the entire block from advance-read (in theory
 | 
						|
         must always be able to get none or all since on block boundaries */
 | 
						|
 | 
						|
      if ( opt->advanceLargeBufferAvail != AR4EMPTY )
 | 
						|
         if ( file == opt->advanceReadFile )
 | 
						|
         {
 | 
						|
            len = (unsigned)(opt->blockSize) ;
 | 
						|
 | 
						|
            /* if an overlap of any type occurs, then copy that part out first,
 | 
						|
               and then take care of the remaining pieces */
 | 
						|
            if ( pos >= opt->advanceLargePos && pos < opt->advanceLargePos + (long)opt->advanceLargeLen )
 | 
						|
            {
 | 
						|
               if ( pos == opt->advanceLargePos )  /* easy case */
 | 
						|
               {
 | 
						|
                  if ( opt->advanceLargeLen >= len )
 | 
						|
                  {
 | 
						|
                     while( opt->advanceLargeBufferAvail == AR4SET )  /* wait for the buffer to complete read, if reqd. */
 | 
						|
                        Sleep( 0 ) ;
 | 
						|
                     if ( opt->advanceLargeBufferAvail == AR4FULL ) /* successful read */
 | 
						|
                     {
 | 
						|
                        memcpy( buf, opt->advanceLargeBuffer, len ) ;
 | 
						|
                        /* reset the position of the data for easier handling later */
 | 
						|
                        opt->advanceLargePos += len ;
 | 
						|
                        opt->advanceLargeLen -= len ;
 | 
						|
                        memcpy( opt->advanceLargeBuffer, opt->advanceLargeBuffer + len, opt->advanceLargeLen ) ;
 | 
						|
                        if ( opt->advanceLargeLen == 0 )  /* just remove */
 | 
						|
                        {
 | 
						|
                           opt->advanceLargeBufferAvail = AR4EMPTY ;
 | 
						|
                           opt->advanceReadFile = 0 ;
 | 
						|
                        }
 | 
						|
                        return len ;
 | 
						|
                     }
 | 
						|
                  }
 | 
						|
               }
 | 
						|
               else
 | 
						|
                  if ( pos > opt->advanceLargePos )  /* full copy only */
 | 
						|
                  {
 | 
						|
                     copyOffset = pos - opt->advanceLargePos ;
 | 
						|
                     compareLen = opt->advanceLargeLen - copyOffset ;
 | 
						|
                     if ( compareLen >= len )  /* then ok */
 | 
						|
                     {
 | 
						|
                        while( opt->advanceLargeBufferAvail == AR4SET )  /* wait for the buffer to complete read, if reqd. */
 | 
						|
                           Sleep( 0 ) ;
 | 
						|
                        if ( opt->advanceLargeBufferAvail == AR4FULL ) /* successful read */
 | 
						|
                        {
 | 
						|
                           if ( compareLen == len )
 | 
						|
                              opt->advanceLargeLen -= compareLen ;   /* remove trailer, since will get copied into buffers anyway */
 | 
						|
                           memcpy( buf, opt->advanceLargeBuffer + copyOffset, len ) ;
 | 
						|
                           return len ;
 | 
						|
                        }
 | 
						|
                     }
 | 
						|
                  }
 | 
						|
            }
 | 
						|
         }
 | 
						|
   #endif
 | 
						|
 | 
						|
   if ( opt->writeFile == file )
 | 
						|
      if ( (unsigned long)pos + opt->blockSize >= opt->writeStartPos && (unsigned long) pos < opt->writeCurPos )
 | 
						|
         opt4flushWriteBuffer( opt ) ;
 | 
						|
 | 
						|
   len = file4readLow( file, pos, buf, (unsigned)(opt->blockSize) ) ;
 | 
						|
 | 
						|
   return len ;
 | 
						|
}
 | 
						|
 | 
						|
/* this function performs larger reads into the given buffer with the
 | 
						|
   desired length to read.   This is used for advance reads and when
 | 
						|
   larger amounts of information are required
 | 
						|
   returned is the length read */
 | 
						|
static unsigned long opt4fileReadToBuffer( FILE4 *file, char *buf, unsigned long pos, unsigned int readLen )
 | 
						|
{
 | 
						|
   unsigned long len, saveBlockSize ;
 | 
						|
   OPT4 *opt ;
 | 
						|
 | 
						|
   opt = &file->codeBase->opt ;
 | 
						|
   saveBlockSize = opt->blockSize ;
 | 
						|
   opt->blockSize = readLen ;
 | 
						|
   len = opt4fileReadFile( file, pos, buf ) ;
 | 
						|
   opt->blockSize = saveBlockSize ;
 | 
						|
 | 
						|
   return len ;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef S4ADVANCE_READ
 | 
						|
void S4CALL opt4readCompletionRoutine( void *advance )
 | 
						|
{
 | 
						|
   FILE4ADVANCE_READ *advanceRead ;
 | 
						|
   int *arFlag ;
 | 
						|
 | 
						|
   /* to verify safety, use critical section on the arFlag (from CODE4) */
 | 
						|
 | 
						|
   advanceRead = (FILE4ADVANCE_READ *)advance ;
 | 
						|
   arFlag = (int *)(advanceRead->completionData) ;
 | 
						|
   if ( advanceRead->usageFlag == r4canceled )
 | 
						|
      *arFlag = AR4EMPTY ;
 | 
						|
   else
 | 
						|
      if ( *arFlag == AR4SET )  /* if reset to empty it means the read was cancelled by the main thread, so leave as empty */
 | 
						|
      {
 | 
						|
         /* verify that all was read */
 | 
						|
         if ( advanceRead->file->codeBase->opt.advanceLargeLen == advanceRead->status )
 | 
						|
            *arFlag = AR4FULL ;
 | 
						|
         else
 | 
						|
            *arFlag = AR4EMPTY ;
 | 
						|
      }
 | 
						|
}
 | 
						|
 | 
						|
static void opt4fileReadAdvanceBuffer( OPT4 *opt, FILE4 *f4, void *buffer, long pos, unsigned int len )
 | 
						|
{
 | 
						|
   long hashVal ;
 | 
						|
 | 
						|
   hashVal = opt4fileHash( opt, f4, (unsigned long)pos ) ;
 | 
						|
   if ( opt4fileReturnBlock( f4, pos, hashVal ) != 0 )  /* in memory, so don't perform the advance-read */
 | 
						|
      return ;
 | 
						|
 | 
						|
   opt->advanceLargeBufferAvail = AR4SET ;
 | 
						|
   opt->advanceReadFile = f4 ;
 | 
						|
   file4advanceRead( f4, pos, buffer, len, opt4readCompletionRoutine, &opt->advanceLargeBufferAvail ) ;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/* this function does advance reads by reading in a whole buffer at
 | 
						|
   a time, instead of just one block worth
 | 
						|
   will advance-read numBlocks blocks.  If numBlocks == -1, then will advance
 | 
						|
   read the whole read buffer, if direction == -1, then it self-detects it
 | 
						|
   if either direction or numBlocks is -1, both must be -1 */
 | 
						|
static void opt4fileReadSpBuffer( FILE4 *file, const unsigned long posIn, int numBlocks, int direction )
 | 
						|
{
 | 
						|
   unsigned long len, curPos, saveBlockSize, endPos, pos ;
 | 
						|
   unsigned short copyPos ;
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   OPT4 *opt ;
 | 
						|
   long hashVal ;
 | 
						|
   int readBlocks ;
 | 
						|
   unsigned int curBlocks ;
 | 
						|
   #ifdef S4ADVANCE_READ
 | 
						|
      int posInAdvance ;  /* position to read was within the advance-read area */
 | 
						|
      int blocksWithin ;  /* the number of blocks within the advance read once position-adjusted (i.e. the length relevanet to quantity reqd. - if numBlocks not -1 */
 | 
						|
      int advanceNextRead ;
 | 
						|
      long advPos ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file == 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm_null, E92508 ) ;
 | 
						|
         return ;
 | 
						|
      }
 | 
						|
      if ( ( direction == -1 && numBlocks != -1 ) || ( direction != -1 && numBlocks == -1 ) )
 | 
						|
      {
 | 
						|
         error4( file->codeBase, e4parm, E92508 ) ;
 | 
						|
         return ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   pos = posIn ;
 | 
						|
   opt = &file->codeBase->opt ;
 | 
						|
   saveBlockSize = opt->blockSize ;
 | 
						|
 | 
						|
   #ifdef S4ADVANCE_READ
 | 
						|
      advanceNextRead = 0 ;
 | 
						|
 | 
						|
      /* first check the advance read-buffer, and see if it matches the
 | 
						|
         information we want.  Even if it doesn't, if numBlocks is -1, then
 | 
						|
         it gets copied out (to perform more advance-reading */
 | 
						|
      if ( opt->advanceLargeBufferAvail == AR4SET ) /* wait on read if file matches */
 | 
						|
         if ( file == opt->advanceReadFile )
 | 
						|
            while( opt->advanceLargeBufferAvail == AR4SET )
 | 
						|
               Sleep( 0 ) ;
 | 
						|
 | 
						|
      if ( opt->advanceLargeBufferAvail == AR4FULL )
 | 
						|
      {
 | 
						|
         if ( opt->advanceLargeLen == 0 )
 | 
						|
         {
 | 
						|
            opt->advanceLargeBufferAvail = AR4EMPTY ;
 | 
						|
            opt->advanceReadFile = 0 ;
 | 
						|
         }
 | 
						|
         else
 | 
						|
         {
 | 
						|
            blocksWithin = 0 ;
 | 
						|
            posInAdvance = 0 ;
 | 
						|
 | 
						|
            if ( file == opt->advanceReadFile )
 | 
						|
            {
 | 
						|
               if ( pos >= (unsigned long)opt->advanceLargePos && pos <= opt->advanceLargePos + opt->advanceLargeLen - saveBlockSize )
 | 
						|
               {
 | 
						|
                  posInAdvance = 1 ;
 | 
						|
                  if ( numBlocks != -1 )
 | 
						|
                     blocksWithin = ( opt->advanceLargeLen + opt->advanceLargePos - pos  ) / saveBlockSize ;
 | 
						|
               }
 | 
						|
               else
 | 
						|
                  posInAdvance = 0 ;
 | 
						|
            }
 | 
						|
 | 
						|
            if ( file == opt->advanceReadFile || numBlocks == -1 )  /* extract the blocks from the advance-read buffer */
 | 
						|
            {
 | 
						|
               advPos = curPos = opt->advanceLargePos ;
 | 
						|
               hashVal = opt4fileHash( opt, file, curPos ) ;
 | 
						|
               opt->advanceLargeBufferAvail = AR4EMPTY ;
 | 
						|
               opt->advanceReadFile = 0 ;
 | 
						|
 | 
						|
               for ( ;; )
 | 
						|
               {
 | 
						|
                  blockOn = opt4fileReturnBlock( file, curPos, hashVal ) ;
 | 
						|
 | 
						|
                  if ( blockOn == 0 )
 | 
						|
                  {
 | 
						|
                     blockOn = opt4fileGetBlock( opt, file, 0 ) ;
 | 
						|
                     memcpy( blockOn->data, opt->advanceLargeBuffer + ( curPos - advPos ), (unsigned)(saveBlockSize) ) ;
 | 
						|
                     opt4blockAdd( blockOn, file, (unsigned)(saveBlockSize), hashVal, curPos ) ;
 | 
						|
                     opt4blockUpgradePriorityCheck( blockOn, opt ) ;
 | 
						|
                  }
 | 
						|
                  else   /* update the lru status */
 | 
						|
                     opt4listLruBottomShift( blockOn ) ;
 | 
						|
 | 
						|
                  curPos += saveBlockSize ;
 | 
						|
 | 
						|
                  #ifdef E4DEBUG
 | 
						|
                     if ( curPos > opt->advanceLargePos + opt->advanceLargeLen )
 | 
						|
                     {
 | 
						|
                        error4( file->codeBase, e4struct, E92508 ) ;
 | 
						|
                        return ;
 | 
						|
                     }
 | 
						|
                  #endif
 | 
						|
 | 
						|
                  if ( curPos == opt->advanceLargePos + opt->advanceLargeLen )
 | 
						|
                     break ;
 | 
						|
 | 
						|
                  hashVal++ ;
 | 
						|
                  if ( (unsigned long)hashVal >= opt->numLists )
 | 
						|
                     hashVal = opt4fileHash( opt, file, curPos ) ;
 | 
						|
               }
 | 
						|
 | 
						|
               if ( file == opt->advanceReadFile )
 | 
						|
               {
 | 
						|
                  if ( numBlocks == -1 )
 | 
						|
                  {
 | 
						|
                     if ( posInAdvance == 1 )
 | 
						|
                        return ;
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                     if ( blocksWithin >= numBlocks )
 | 
						|
                        return ;
 | 
						|
               }
 | 
						|
            }
 | 
						|
         }
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   if ( direction == -1 )
 | 
						|
   {
 | 
						|
      /* first do a sample to see which way to read */
 | 
						|
      blockOn = opt4fileReturnBlock( file, pos+saveBlockSize, opt4fileHash( opt, file, pos+saveBlockSize ) ) ;
 | 
						|
      if ( blockOn != 0 ) /* read previous blocks, not next blocks */
 | 
						|
      {
 | 
						|
         if ( pos > ( opt->bufferSize - saveBlockSize ) )
 | 
						|
            pos -= ( opt->bufferSize - saveBlockSize ) ;
 | 
						|
         else
 | 
						|
            pos = 0 ;
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   readBlocks = 0 ;
 | 
						|
   curBlocks = opt->maxBlocks ;
 | 
						|
 | 
						|
   for ( ;; )  /* may need to read more than the read-buffer worth if a large read */
 | 
						|
   {
 | 
						|
      if ( numBlocks != -1 )
 | 
						|
      {
 | 
						|
         if ( curBlocks > (unsigned int)( numBlocks - readBlocks ) )  /* don't over-read */
 | 
						|
         {
 | 
						|
            curBlocks = numBlocks - readBlocks ;
 | 
						|
            numBlocks = -1 ;   /* will be done */
 | 
						|
         }
 | 
						|
      }
 | 
						|
      len = opt4fileReadToBuffer( file, opt->readBuffer, pos, (unsigned int)( curBlocks * saveBlockSize ) ) ;
 | 
						|
      if ( len == 0 )
 | 
						|
         return ;
 | 
						|
 | 
						|
      opt->readStartPos = pos ;
 | 
						|
      opt->readFile = file ;
 | 
						|
 | 
						|
      /* first do the last block, in case there is a length issue */
 | 
						|
      curPos = pos + saveBlockSize * ( ( len - 1 ) >> opt->blockPower ) ;
 | 
						|
      hashVal = opt4fileHash( opt, file, curPos ) ;
 | 
						|
      blockOn = opt4fileReturnBlock( file, curPos, hashVal ) ;
 | 
						|
      if ( blockOn == 0 )
 | 
						|
      {
 | 
						|
         blockOn = opt4fileGetBlock( opt, file, 0 ) ;
 | 
						|
         endPos = curPos - pos ;
 | 
						|
         copyPos = (unsigned short)(len - endPos) ;
 | 
						|
         memcpy( blockOn->data, opt->readBuffer + endPos, copyPos ) ;
 | 
						|
         opt4blockAdd( blockOn, file, copyPos, hashVal, curPos ) ;
 | 
						|
         opt4blockUpgradePriorityCheck( blockOn, opt ) ;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( curPos != pos )
 | 
						|
      {
 | 
						|
         curPos -= saveBlockSize ;
 | 
						|
 | 
						|
         /* can just subtract one from hash value since it goes in order */
 | 
						|
         for ( ;; curPos -= saveBlockSize )
 | 
						|
         {
 | 
						|
            hashVal-- ;
 | 
						|
            if ( hashVal < 0 )
 | 
						|
               hashVal = opt4fileHash( opt, file, curPos ) ;
 | 
						|
            blockOn = opt4fileReturnBlock( file, curPos, hashVal ) ;
 | 
						|
            if ( blockOn == 0 )
 | 
						|
            {
 | 
						|
               blockOn = opt4fileGetBlock( opt, file, 0 ) ;
 | 
						|
               memcpy( blockOn->data, opt->readBuffer + ( curPos - pos ), (unsigned)(saveBlockSize) ) ;
 | 
						|
               opt4blockAdd( blockOn, file, (unsigned)(saveBlockSize), hashVal, curPos ) ;
 | 
						|
               opt4blockUpgradePriorityCheck( blockOn, opt ) ;
 | 
						|
            }
 | 
						|
            else   /* update the lru status */
 | 
						|
               opt4listLruBottomShift( blockOn ) ;
 | 
						|
            if ( curPos == pos )
 | 
						|
            {
 | 
						|
               #ifdef S4ADVANCE_READ
 | 
						|
                  advanceNextRead = 1 ;
 | 
						|
               #endif
 | 
						|
               break ;
 | 
						|
            }
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      opt->readFile = 0 ;
 | 
						|
 | 
						|
      if ( numBlocks == -1 )
 | 
						|
         break ;
 | 
						|
      if ( len < opt->bufferSize )
 | 
						|
         break ;
 | 
						|
 | 
						|
      readBlocks += curBlocks ;
 | 
						|
      pos += opt->bufferSize ;
 | 
						|
   }
 | 
						|
 | 
						|
   #ifdef S4ADVANCE_READ
 | 
						|
      if ( opt->advanceLargeBufferAvail == AR4EMPTY && advanceNextRead == 1 && numBlocks == -1 )  /* request an advance read for the next section */
 | 
						|
      {
 | 
						|
         if ( direction == 1 )
 | 
						|
            advPos = ((unsigned long)(posIn >> opt->blockPower ) >> opt->blockPower ) + opt->bufferSize ;
 | 
						|
         else
 | 
						|
            advPos = ((unsigned long)(posIn << opt->blockPower ) >> opt->blockPower ) - 2 * opt->bufferSize ;
 | 
						|
 | 
						|
         if ( advPos > 0 )
 | 
						|
         {
 | 
						|
            opt->advanceLargePos = advPos ;
 | 
						|
            opt->advanceLargeLen = opt->bufferSize ;
 | 
						|
            opt4fileReadAdvanceBuffer( opt, file, opt->advanceLargeBuffer, advPos, opt->bufferSize ) ;
 | 
						|
         }
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   return ;
 | 
						|
}
 | 
						|
 | 
						|
OPT4BLOCK *opt4fileReturnBlock( FILE4 *file, long pos, long hashVal )
 | 
						|
{
 | 
						|
   OPT4CMP compare ;
 | 
						|
   OPT4BLOCK *blockOn ;
 | 
						|
   LIST4 *list ;
 | 
						|
 | 
						|
   #ifdef E4PARM_LOW
 | 
						|
      if ( file->codeBase->opt.numBuffers == 0 || file == 0  || hashVal < 0 || pos < 0 )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
      if ( (unsigned long)hashVal >= file->codeBase->opt.numLists )
 | 
						|
      {
 | 
						|
         error4( 0, e4parm, E92508 ) ;
 | 
						|
         return 0 ;
 | 
						|
      }
 | 
						|
   #endif
 | 
						|
 | 
						|
   if ( file->doBuffer == 0 )
 | 
						|
      return 0 ;
 | 
						|
 | 
						|
   list = &file->codeBase->opt.lists[hashVal] ;
 | 
						|
   blockOn = (OPT4BLOCK *)l4first( list ) ;
 | 
						|
   if ( blockOn != 0 )
 | 
						|
   {
 | 
						|
      compare.file = file ;
 | 
						|
      memcpy( (void *)&compare.pos, (void *)&pos, sizeof( compare.pos ) ) ;
 | 
						|
 | 
						|
      for( ;; )
 | 
						|
      {
 | 
						|
         if ( !c4memcmp( (void *)&blockOn->file, (void *)&compare, sizeof( compare ) ) )
 | 
						|
            return blockOn ;
 | 
						|
         blockOn = (OPT4BLOCK *)l4next( list, blockOn ) ;
 | 
						|
         if ( blockOn == 0 )
 | 
						|
            return 0 ;
 | 
						|
      }
 | 
						|
   }
 | 
						|
   return 0 ;
 | 
						|
}
 | 
						|
 | 
						|
#endif   /* not S4OPTIMIZE_OFF */
 |