/* d4opt.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */

#include "d4all.h"
#ifndef S4UNIX
   #ifdef __TURBOC__
      #pragma hdrstop
   #endif
#endif

#ifndef S4OFF_OPTIMIZE
   /* ensure we have at least 8 buffers -- 4 for special purpose, 4 for general purpose */
   #define MEM4MIN_BUFFERS 8
   static void opt4freeAlloc( OPT4 * ) ;
   static int opt4initAlloc( CODE4 *, int ) ;
   #ifdef E4ANALYZE_ALL
      int file4copyx( CODE4 *, FILE4 *, char * ) ;
   #endif
#endif

#ifndef S4OFF_OPTIMIZE
/* if possible, it gets the physical amount of available memory and sets the
   memStartMax to a percentage of that amount (the input percent)
   It won't auto-start optimizatino if it was suspended physically */
void code4memStartMaxSet( CODE4 *c4, const int percent )
{
   double availMem ;
   #ifdef S4WIN32
      MEMORYSTATUS memory;
   #endif

   if ( c4->hadOpt == 1 || c4->opt.numBuffers || c4->hasOpt )   /* don't enable if suspend was requested or is already initialized */
      return ;

   availMem = 0 ;

   #ifdef S4WIN32
      GlobalMemoryStatus( &memory ) ;
      availMem = memory.dwAvailPhys ;
   #endif

   if ( availMem != 0 && percent <= 100 && percent >= 0 )
   {
      c4->memStartMax = (long) ( ((double)percent / 100) * availMem ) ;
      code4optStart( c4 ) ;
   }
   else
      c4->hadOpt = 1 ;    /* had chance to enable optimization but it was denied */

   return ;
}
#endif

#ifndef S4CLIENT
#ifdef S4CB51
int S4FUNCTION code4freeBlocks( CODE4 *c4 )
#else
static int code4freeBlocks( CODE4 *c4 )
#endif
{
   #ifdef S4SERVER
      SERVER4CLIENT *client ;
   #endif
   #ifndef S4CLIENT
      DATA4 *data ;
   #endif

   #ifdef E4PARM_HIGH
      if ( c4 == 0 )
         return error4( 0, e4parm_null, E92510 ) ;
   #endif

   #ifdef S4SERVER
      for( client = 0 ;; )
      {
         client = (SERVER4CLIENT *)l4next( &c4->server->clients, client ) ;
         if ( client == 0 )
            break ;
         for ( data = 0 ;; )
         {
            data = (DATA4 *)l4next( tran4dataList( &client->trans ), data ) ;
            if ( data == 0 )
               break ;
            d4freeBlocks( data ) ;
         }
      }
   #endif

   #ifdef S4STAND_ALONE
      for ( data = 0 ;; )
      {
         data = (DATA4 *)l4next( tran4dataList( &c4->c4trans.trans ), data ) ;
         if ( data == 0 )
            break ;
         d4freeBlocks( data ) ;
      }
   #endif

   return 0 ;
}
#endif

#ifdef S4STAND_ALONE
#ifndef S4OFF_OPTIMIZE
int S4FUNCTION code4optAll( CODE4 *c4 )
{
   LIST4 *list ;
   int rc ;
   DATA4FILE *dfile ;
   DATA4 *data ;

   #ifdef E4PARM_HIGH
      if ( c4 == 0 )
         return error4( 0, e4parm_null, E92531 ) ;
   #endif

   list = tran4dataList( code4trans( c4 ) ) ;
   for ( dfile = 0 ;; )
   {
      dfile = (DATA4FILE *)l4next( &c4->dataFileList, dfile ) ;
      if ( dfile == 0 )
         break ;
      #ifdef S4OPTIMIZE_STATS
         /* don't optimize the stat file or nested d4appends occur */
         if ( c4->statusDbf != 0 )
            if ( &dfile->file == &c4->statusDbf->dataFile->file )  /* don't do for the stat file! */
               continue ;
      #endif
      for ( data = 0 ;; )
      {
         data = (DATA4 *)l4next( list, data ) ;
         if ( data == 0 )
         {
            #ifdef E4ANALYZE
               #ifndef S4OFF_MULTI
                  code4lockClear( c4 ) ;
               #endif
               return error4( c4, e4info, E92531 ) ;
            #else
               break ;
            #endif
         }
         if ( data->dataFile == dfile )
         {
            #ifndef S4OFF_MULTI
               rc = d4lockAddAll( data ) ;
               if ( rc != 0 )
                  return rc ;
            #endif
            rc = d4optimize( data, OPT4ALL ) ;
            if ( rc != 0 )
               return rc ;
            rc = d4optimizeWrite( data, OPT4ALL ) ;
            if ( rc != 0 )
               return rc ;
            break ;
         }
      }
   }

   #ifndef S4OFF_MULTI
      rc = code4lock( c4 ) ;
      if ( rc != 0 )
         return rc ;
   #endif

   code4optStart( c4 ) ;

   return 0 ;
}
#endif /* S4OFF_OPTIMIZE */
#endif /* S4STAND_ALONE */

int S4FUNCTION code4optStart( CODE4 *c4 )
{
   #ifndef S4OFF_OPTIMIZE
      int rc ;

      #ifdef E4PARM_HIGH
         if ( c4 == 0 )
            return error4( 0, e4parm_null, E92501 ) ;
      #endif

      rc = code4optRestart( c4 ) ;
      #ifndef S4CLIENT
         if ( rc == 0 )
            code4freeBlocks( c4 ) ;
      #endif

      return rc ;
   #else
      return 0 ;
   #endif /* S4OFF_OPTIMIZE */
}

int code4optRestart( CODE4 *c4 )
{
   #ifndef S4OFF_OPTIMIZE
      OPT4 *opt ;
      unsigned numBuffers ;
      int numAlloc ;
      FILE4 *fileOn ;
      double hitCountAdd ;
      #ifdef S4WIN32
         DWORD sectorsPerCluster, bytesPerSector, numberFreeClusters, totalNumberClusters ;
      #endif

      #ifdef S4VBASIC
         if ( c4parm_check( c4, 1, E92502 ) )
            return -1 ;
      #endif  /* S4VBASIC */

      #ifdef E4PARM_HIGH
         if ( c4 == 0 )
            return error4( 0, e4parm_null, E92502 ) ;
      #endif

      #ifdef E4ANALYZE
         if ( c4->debugInt != 0x5281 )
            return error4( 0, e4struct, E81301 ) ;
      #endif

      if ( error4code( c4 ) < 0 )
         return e4codeBase ;

      opt = &c4->opt ;

      if ( opt->numBuffers || c4->hasOpt )  /* no initialization required */
         return 0 ;

      #ifdef S4WIN32
         /* set block sizes based on sector-size values */
         /* if used, final version must address different drives for different
            files */
         /* use current directory for now */
         if ( GetDiskFreeSpace( 0, &sectorsPerCluster, &bytesPerSector, &numberFreeClusters, &totalNumberClusters ) == TRUE )
         {
            if ( c4->memSizeBlock < bytesPerSector )  /* ok if larger than sector size */
               c4->memSizeBlock = bytesPerSector ;
            else
            {
               /* round down to nearest multiple sector size */
               c4->memSizeBlock = bytesPerSector * (c4->memSizeBlock / bytesPerSector ) ;
               /* round down to nearest multiple block size */
               c4->memSizeBuffer = c4->memSizeBlock * ( c4->memSizeBuffer / c4->memSizeBlock ) ;
            }
            if ( c4->memSizeBlock > c4->memSizeBuffer )
               c4->memSizeBuffer = c4->memSizeBlock ;
         }
      #endif

      #ifdef E4ANALYZE
         if ( c4->memSizeBlock == 0 || c4->memSizeBuffer == 0 || c4->memSizeBlock > c4->memSizeBuffer )
            return error4( c4, e4struct, E82501 ) ;
      #endif

      opt->blockSize = c4->memSizeBlock ;

      if ( c4->memSizeBlock != 0 )
         opt->bufferSize = (unsigned long)(c4->memSizeBlock * ( (unsigned long)c4->memSizeBuffer / c4->memSizeBlock )) ;
      else
         opt->bufferSize = 0 ;

      opt->hashTrail = 0 ;
      opt->prio[0] = &opt->other ;
      opt->prio[1] = &opt->dbfLo ;
      opt->prio[2] = &opt->indexLo ;
      opt->prio[3] = &opt->dbfHi ;
      opt->prio[4] = &opt->indexHi ;
      opt->doUpdate = 1 ;
      opt->checkCount = OPT4CHECK_RATE ;   /* set to do analysis when first block removed */

      for( ; ( (numBuffers = (unsigned int)((unsigned long)c4->memStartMax / (unsigned long)(opt->bufferSize - 2UL) )) < MEM4MIN_BUFFERS) ; )
      {
         opt->bufferSize -= opt->blockSize ;
         if ( opt->bufferSize == 0 )
            return -1 ;
      }

      numBuffers -= 4 ;   /* 4 special use buffers are taken into account elsewhere */

      for( ;; )
      {
         c4->hasOpt = 1 ;
         opt->minLink = opt->maxBlocks = (unsigned int)( opt->bufferSize / opt->blockSize ) ;
         opt->blockPower = (char)c4calcType( opt->blockSize ) ;
         opt->numShift = (char)(8*sizeof( long ) - (opt->blockPower)) ;
         opt->numLists = OPT4BLOCK_DENSITY << c4calcType( (long)numBuffers * opt->maxBlocks ) ;

         numAlloc = opt4initAlloc( c4, numBuffers ) ;

         if ( numAlloc <= 0 )
         {
            code4optSuspend( c4 ) ;
            return -1 ;
         }

         opt->numBuffers = numAlloc ;

         if ( numAlloc < 4 )   /* couldn't do a minimum allocation, try again */
         {
            opt4freeAlloc( opt ) ;   /* free allocs */
            if ( numBuffers > 4 )
               numBuffers = 4 ;
            opt->bufferSize /= 2 ;
            opt->bufferSize = opt->bufferSize - opt->bufferSize % opt->blockSize ;  /* round it down to a blockSize multiple */
            if ( opt->bufferSize == 0 )
               return -1 ;
            continue ;
         }
         break ;
      }

      opt->numBlocks = (unsigned long)opt->numBuffers * opt->maxBlocks ;
      opt->numLists = OPT4BLOCK_DENSITY << c4calcType( opt->numBlocks ) ;
      opt->mask = opt->numLists - 1 ;

      /* now actually optimize those files reqd */
      for ( fileOn = 0 ;; )
      {
         fileOn = (FILE4 *)l4next( &opt->optFiles, fileOn ) ;
         if ( fileOn == 0 )
            break ;
         fileOn->len = -1 ;   /* in case the file length changed during suspension */
         fileOn->doBuffer = 1 ;   /* re-add the reference */
         #ifndef S4SINGLE
            if ( fileOn->lowAccessMode == OPEN4DENY_RW )
         #endif
         file4setWriteOpt( fileOn, 1 ) ;

         if ( fileOn->type == OPT4DBF )
         {
            hitCountAdd = (double)fileOn->expectedReadSize / (double)opt->blockSize ;
            if ( hitCountAdd > 1.0 )
                fileOn->hitCountAdd = 1.0 ;
             else
                fileOn->hitCountAdd = hitCountAdd ;
         }

         if ( fileOn->hashInit == -1 )
         {
            fileOn->hashInit = opt->hashTrail * opt->blockSize ;
            #ifdef E4ANALYZE
               if ( ( (long)file4len( fileOn ) < (long)0L ) || ( opt->blockSize == 0 ) )
                  return error4( c4, e4info, E92502 ) ;
            #endif
            opt->hashTrail = (opt->hashTrail + (unsigned)file4len( fileOn ) / opt->blockSize) % opt->numBlocks ;
         }
         #ifdef E4ANALYZE_ALL
            file4copyx( c4, fileOn, fileOn->dupName ) ;
         #endif
      }

      opt->minAccessTimeVariation = (unsigned int)opt->numBlocks / 100 ;
      if ( opt->minAccessTimeVariation < 2 )  /* provide a basic minimum */
         opt->minAccessTimeVariation = 2 ;
      opt->dbfLo.minLink = (unsigned short int) ((double)opt->numBlocks * OPT4DBF_LO_MIN_LINK) ;
      opt->dbfLo.maxTime = (unsigned long) ((double)opt->numBlocks * OPT4DBF_LO_MAX_TIME) ;
      opt->dbfLo.minTime = (unsigned long) ((double)opt->numBlocks * OPT4DBF_LO_MIN_TIME) ;
      opt->dbfHi.minLink = (unsigned short int) ((double)opt->numBlocks * OPT4DBF_HI_MIN_LINK) ;
      opt->dbfHi.maxTime = (unsigned long) ((double)opt->numBlocks * OPT4DBF_HI_MAX_TIME) ;
      opt->dbfHi.minTime = (unsigned long) ((double)opt->numBlocks * OPT4DBF_HI_MIN_TIME) ;
      opt->indexLo.minLink = (unsigned short int) ((double)opt->numBlocks * OPT4INDEX_LO_MIN_LINK) ;
      opt->indexLo.maxTime = (unsigned long) ((double)opt->numBlocks * OPT4INDEX_LO_MAX_TIME) ;
      opt->indexLo.minTime = (unsigned long) ((double)opt->numBlocks * OPT4INDEX_LO_MIN_TIME) ;
      opt->indexHi.minLink = (unsigned short int) ((double)opt->numBlocks * OPT4INDEX_HI_MIN_LINK) ;
      opt->indexHi.maxTime = (unsigned long) ((double)opt->numBlocks * OPT4INDEX_HI_MAX_TIME) ;
      opt->indexHi.minTime = (unsigned long) ((double)opt->numBlocks * OPT4INDEX_HI_MIN_TIME) ;
      opt->other.minLink = (unsigned short int) ((double)opt->numBlocks * OPT4OTHER_MIN_LINK) ;
      opt->other.maxTime = (unsigned long) ((double)opt->numBlocks * OPT4OTHER_MAX_TIME) ;
      opt->other.minTime = (unsigned long) ((double)opt->numBlocks * OPT4OTHER_MIN_TIME) ;

   #endif /* S4OFF_OPTIMIZE */
   return 0 ;
}

int S4FUNCTION code4optSuspend( CODE4 *c4 )
{
   #ifndef S4OFF_OPTIMIZE
      OPT4 *opt ;
      FILE4 *fileOn ;
      int rc, saveRc ;

      #ifdef S4VBASIC
         if ( c4parm_check( c4, 1, E92503 ) )
            return -1 ;
      #endif  /* S4VBASIC */

      #ifdef E4PARM_HIGH
         if ( c4 == 0 )
            return error4( 0, e4parm_null, E92503 ) ;
      #endif

      opt = &c4->opt ;
      if ( opt->numBuffers == 0 || c4->hasOpt == 0 )
         return 0 ;

      rc = 0 ;
      saveRc = error4set( c4, 0 ) ;

      /* first remove any optimized files */
      for ( fileOn = 0 ;; )
      {
         fileOn = (FILE4 *)l4next( &opt->optFiles, fileOn ) ;
         if ( fileOn == 0 )
            break ;
         rc = opt4fileFlush( fileOn, 1 ) ;
         fileOn->doBuffer = 0 ;  /* remove the reference */
         file4setWriteOpt( fileOn, 0 ) ;
      }
      c4->hasOpt = 0 ;
      c4->hadOpt = 1 ;   /* indicate that at one time optimization was enabled */

      opt4freeAlloc( opt ) ;

      opt->numBuffers = 0 ;  /* mark as freed */

      if ( saveRc < 0 )
         error4set( c4, saveRc ) ;

      if ( rc < 0 )
         return error4stack( c4, e4optSuspend, E92503 ) ;
      else
   #endif
   return 0 ;
}

#ifndef S4SERVER
int S4FUNCTION d4optimize( DATA4 *d4, const int optFlag )
{
   #ifdef S4CLIENT
      return 0 ;
   #else
      #ifdef S4VBASIC
         if ( c4parm_check( d4, 2, E92504 ) )
            return -1 ;
      #endif  /* S4VBASIC */

      #ifdef E4PARM_HIGH
         if ( d4 == 0 || optFlag < -1 || optFlag > 1 )
            return error4( 0, e4parm, E92504 ) ;
      #endif
      return dfile4optimize( d4->dataFile, optFlag ) ;
   #endif
}
#endif /* S4SERVER */

#ifndef S4CLIENT
int dfile4optimize( DATA4FILE *d4, const int optFlag )
{
   #ifndef S4OFF_OPTIMIZE
      int rc ;
      #ifndef S4OFF_INDEX
         #ifdef N4OTHER
            TAG4FILE *tagOn ;
         #else
            INDEX4FILE *indexOn ;
         #endif
      #endif

      #ifdef E4PARM_LOW
         if ( d4 == 0 || optFlag < -1 || optFlag > 1 )
            return error4( 0, e4parm, E91102 ) ;
      #endif

      if ( dfile4recWidth( d4 ) > d4->c4->opt.bufferSize )  /* don't optimize records larger than the buffer size */
      {
         rc = file4optimizeLow( &d4->file, optFlag, OPT4DBF, dfile4recWidth( d4 ), d4 ) ;
         if ( rc < 0 )
            return rc ;
      }

      #ifndef S4OFF_INDEX
         #ifdef N4OTHER
            for( tagOn = 0;;)
            {
               tagOn = dfile4tagNext( d4, tagOn ) ;
               if ( tagOn == 0 )
                  break ;

               rc = file4optimizeLow( &tagOn->file, optFlag, OPT4INDEX, 0, tagOn ) ;
               if ( rc < 0 )
                  return rc ;
            }
         #else
            for ( indexOn = 0 ;; )
            {
               indexOn = (INDEX4FILE *)l4next( &d4->indexes, indexOn ) ;
               if ( indexOn == 0 )
                  break ;
               rc = file4optimizeLow( &indexOn->file, optFlag, OPT4INDEX, 0, indexOn ) ;
               if ( rc < 0 )
                  return rc ;
            }
         #endif
      #endif

      #ifndef S4OFF_MEMO
         if ( d4->memoFile.file.hand != -1 )
            return file4optimize( &d4->memoFile.file, optFlag, OPT4OTHER ) ;
      #endif

      return 0 ;
   #else
      return 0 ;
   #endif
}
#endif

int S4FUNCTION d4optimizeWrite( DATA4 *d4, const int optFlag )
{
   #ifdef S4CLIENT
      return 0 ;
   #else
      #ifdef S4VBASIC
         if ( c4parm_check( d4, 2, E92506 ) )
            return -1 ;
      #endif  /* S4VBASIC */

      #ifdef E4PARM_HIGH
         if ( d4 == 0 || optFlag < -1 || optFlag > 1 )
            return error4( 0, e4parm, E92506 ) ;
      #endif
      return dfile4optimizeWrite( d4->dataFile, optFlag ) ;
   #endif
}

#ifndef S4CLIENT
#ifdef P4ARGS_USED
   #pragma argsused
#endif
int dfile4optimizeWrite( DATA4FILE *d4, const int optFlag )
{
   #ifndef S4OFF_WRITE
      #ifndef S4OFF_OPTIMIZE
         int rc ;
         #ifndef S4OFF_INDEX
            #ifdef N4OTHER
               TAG4FILE *tagOn ;
            #else
               INDEX4FILE *indexOn ;
            #endif
         #endif

         #ifdef E4PARM_LOW
            if ( d4 == 0 || optFlag < -1 || optFlag > 1 )
               return error4( 0, e4parm, E91102 ) ;
         #endif

         rc = file4optimizeWrite( &d4->file, optFlag ) ;
         if ( rc < 0 )
            return error4stack( d4->c4, rc, E91102 ) ;

         #ifndef S4OFF_MEMO
            if ( d4->memoFile.file.hand != -1 )
            {

               rc = file4optimizeWrite( &d4->memoFile.file, optFlag ) ;
               if ( rc < 0 )
                  return error4stack( d4->c4, rc, E91102 ) ;
            }
         #endif

         #ifndef S4OFF_INDEX
            #ifndef N4OTHER
               indexOn = (INDEX4FILE *) l4first( &d4->indexes ) ;
               if ( indexOn != 0 )
                  do
                  {
                     rc = file4optimizeWrite( &indexOn->file, optFlag ) ;
                     if ( rc < 0 )
                        return error4stack( d4->c4, rc, E91102 ) ;
                     indexOn = (INDEX4FILE *)l4next( &d4->indexes, indexOn ) ;
                  } while ( indexOn != 0 ) ;
            #else
               for( tagOn = 0;;)
               {
                  tagOn = dfile4tagNext( d4, tagOn ) ;
                  if ( tagOn == 0 )
                     break ;
                  rc = file4optimizeWrite( &tagOn->file, optFlag ) ;
                  if ( rc < 0 )
                     return error4stack( d4->c4, rc, E91102 ) ;
               }
            #endif
         #endif
      #endif
   #endif
   return 0 ;
}
#endif

#ifndef S4OFF_OPTIMIZE
static void opt4freeAlloc( OPT4 *opt )
{
   OPT4BLOCK *curBlock ;
   int i ;
   #ifdef S4ADVANCE_READ
      FILE4ADVANCE_READ *advanceRead ;
      LINK4 *advanceLink ;
      FILE4 *f4 ;
   #endif

   #ifdef E4PARM_LOW
      if ( opt == 0 )
      {
         error4( 0, e4parm_null, E92508 ) ;
         return ;
      }
   #endif

   #ifdef S4WRITE_DELAY
      if ( opt->delayWriteBuffer != 0 )
      {
         while ( l4numNodes( &opt->delayAvail ) != opt->maxBlocks )  /* wait for delay-write to finish on blocks */
            Sleep( 0 ) ;

         for ( i = 0 ; i < (int)opt->maxBlocks ; i++ )
         {
            curBlock = &opt->blocks[ opt->numBuffers * opt->maxBlocks + i] ;
            l4remove( &opt->delayAvail, &curBlock->lruLink ) ;
         }

         DeleteCriticalSection( &opt->critical4optWrite ) ;
         u4free( opt->delayWriteBuffer ) ;
         opt->delayWriteBuffer = 0 ;
      }
   #endif

   #ifdef S4ADVANCE_READ
      if ( opt->advanceLargeBuffer != 0 )
      {
         /* just cancel it */
         EnterCriticalSection( &opt->critical4optRead ) ;
         f4 = opt->advanceReadFile ;
         if ( f4 != 0 )
            if ( l4numNodes( &f4->advanceReadFileList ) != 0 )
            {
               EnterCriticalSection( &f4->critical4file ) ;
               for ( advanceLink = (LINK4 *)l4first( &f4->advanceReadFileList ) ;; )
               {
                  if ( advanceLink == 0 )
                     break ;
                  advanceRead = (FILE4ADVANCE_READ *)(advanceLink - 1 ) ;
                  advanceLink = (LINK4 *)l4next( &f4->advanceReadFileList, advanceLink ) ;

                  if ( advanceRead->data == opt->advanceLargeBuffer )  /* found the one we want */
                     break ;
               }

               if ( advanceRead != 0 )  /* have the advance-read, just remove it */
               {
                  while ( advanceRead->usageFlag == r4inUse )  /* is being read, so wait */
                     Sleep( 0 ) ;
                  if ( advanceRead->usageFlag == r4queued )  /* remove ourselves */
                  {
                     EnterCriticalSection( &advanceRead->file->codeBase->critical4advanceReadList ) ;
                     l4remove( &advanceRead->file->codeBase->advanceReadList, advanceRead ) ;
                     l4remove( &advanceRead->file->advanceReadFileList, &advanceRead->fileLink ) ;
                     LeaveCriticalSection( &advanceRead->file->codeBase->critical4advanceReadList ) ;
                  }
               }
               LeaveCriticalSection( &f4->critical4file ) ;
            }

         LeaveCriticalSection( &opt->critical4optRead ) ;
         DeleteCriticalSection( &opt->critical4optRead ) ;
         u4free( opt->advanceLargeBuffer ) ;
         opt->advanceLargeBuffer = 0 ;
      }
   #endif

   opt4flushAll( opt, 1 ) ;  /* move all blocks over to avail list */

   if ( opt->buffers )
   {
      for ( --opt->numBuffers; opt->numBuffers >= 0 ; opt->numBuffers-- )
      {
         for ( i = 0 ; i < (int)opt->maxBlocks ; i++ )
         {
            curBlock = &opt->blocks[ opt->numBuffers * opt->maxBlocks + i] ;
            l4remove( &opt->avail, &curBlock->lruLink ) ;
         }
         if ( opt->buffers[opt->numBuffers] != 0 )
            u4free( opt->buffers[opt->numBuffers] ) ;
      }
      u4free( opt->buffers ) ;
      opt->buffers = 0 ;
   }

   opt->writeBuffer = 0 ;
   #ifdef S4WRITE_DELAY
      u4free( opt->delayLargeBuffer ) ;
      opt->delayLargeBuffer = 0 ;
   #endif
   u4free( opt->writeBufferActual ) ;
   opt->writeBufferActual = 0 ;
   u4free( opt->readBuffer ) ;
   opt->readBuffer = 0 ;
   u4free( opt->blocks ) ;
   opt->blocks = 0 ;
   u4free( opt->lists ) ;
   opt->lists = 0 ;
   u4free( opt->lists ) ;
   opt->lists = 0 ;
}

static int opt4initAlloc( CODE4 *c4, int numBuffers )
{
   OPT4BLOCK *curBlock ;
   OPT4 *opt ;
   int numAlloc, i ;

   #ifdef E4PARM_LOW
      if ( c4 == 0 || numBuffers < 0 )
         return error4( c4, e4parm, E92508 ) ;
   #endif

   opt = &c4->opt ;

   opt->buffers = (void **)u4alloc( ( (long)numBuffers ) * sizeof( void * ) ) ;
   if ( opt->buffers == 0 )
   {
      code4optSuspend( c4 ) ;
      return error4stack( c4, e4memory, E92508 ) ;
   }

   opt->lists = (LIST4 *)u4alloc( opt->numLists * sizeof (LIST4) ) ;
   if ( opt->lists == 0 )
   {
      code4optSuspend( c4 ) ;
      return error4stack( c4, e4memory, E92508 ) ;
   }

   #ifdef S4WRITE_DELAY
      #ifdef S4ADVANCE_READ
         opt->blocks = (OPT4BLOCK *)u4alloc( (long)(numBuffers + 2) * opt->maxBlocks * sizeof( OPT4BLOCK ) ) ;
      #else
         opt->blocks = (OPT4BLOCK *)u4alloc( (long)(numBuffers + 1) * opt->maxBlocks * sizeof( OPT4BLOCK ) ) ;
      #endif
   #else
      #ifdef S4ADVANCE_READ
         opt->blocks = (OPT4BLOCK *)u4alloc( (long)(numBuffers + 1) * opt->maxBlocks * sizeof( OPT4BLOCK ) ) ;
      #else
         opt->blocks = (OPT4BLOCK *)u4alloc( (long)numBuffers * opt->maxBlocks * sizeof( OPT4BLOCK ) ) ;
      #endif
   #endif
   if ( opt->blocks == 0 )
   {
      code4optSuspend( c4 ) ;
      return error4stack( c4, e4memory, E92508 ) ;
   }

   opt->writeBufferActual = (char *)u4alloc( opt->bufferSize ) ;
   if( opt->writeBufferActual == 0 )
      return 0 ;
   opt->writeBuffer = opt->writeBufferActual ;

   opt->writeBlockCount = 0 ;
   opt->writeCurPos = 0 ;
   opt->writeStartPos = 0 ;
   opt->writeFile = 0 ;

   opt->readBuffer = (char *)u4alloc( opt->bufferSize ) ;
   if( opt->readBuffer == 0 )
      return 0 ;

   #ifdef S4WRITE_DELAY
      opt->delayWriteBuffer = (char *)u4alloc( opt->bufferSize ) ;
      if( opt->delayWriteBuffer == 0 )
         return 0 ;
      opt->delayLargeBuffer = (char *)u4alloc( opt->bufferSize ) ;
      if( opt->delayLargeBuffer == 0 )
         return 0 ;
      InitializeCriticalSection( &opt->critical4optWrite ) ;
      opt->delayLargeBufferAvail = 1 ;
      opt->writeBufferActualAvail = 1 ;
   #endif
   #ifdef S4ADVANCE_READ
      opt->advanceLargeBuffer = (char *)u4alloc( opt->bufferSize ) ;
      if( opt->advanceLargeBuffer == 0 )
         return 0 ;
      InitializeCriticalSection( &opt->critical4optRead ) ;
      opt->advanceLargeBufferAvail = 1 ;
   #endif

   for ( numAlloc = 0 ; numAlloc < numBuffers ; numAlloc++ )
   {
      opt->buffers[numAlloc] = (void *)u4alloc( opt->bufferSize ) ;
      if( opt->buffers[numAlloc] == 0 )
         break ;
      for ( i = 0 ; i < (int)opt->maxBlocks ; i++ )
      {
         curBlock = &opt->blocks[ numAlloc * opt->maxBlocks + i] ;
         curBlock->data = (OPT4BLOCK *)( (char *)opt->buffers[numAlloc] + i * opt->blockSize ) ;
         l4add( &opt->avail, &curBlock->lruLink ) ;
      }
   }

   #ifdef S4WRITE_DELAY
      /* now set up the delay write buffer. numAlloc already 1 greater than max, so just use */
      for ( i = 0 ; i < (int)opt->maxBlocks ; i++ )
      {
         curBlock = &opt->blocks[ numAlloc * opt->maxBlocks + i] ;
         curBlock->data = (OPT4BLOCK *)( (char *)opt->delayWriteBuffer + i * opt->blockSize ) ;
         l4add( &opt->delayAvail, &curBlock->lruLink ) ;
      }
   #endif

   return numAlloc ;
}

#endif /* S4OFF_OPTIMIZE */