home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / dev / c / Asyncio.lha / AsyncIO / src / SeekAsync.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-16  |  7.1 KB  |  216 lines

  1. #include "async.h"
  2.  
  3. LibCall LONG
  4. SeekAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) LONG position, _REG( d1 ) SeekModes mode )
  5. {
  6. #ifdef ASIO_NOEXTERNALS
  7.     struct DosLibrary    *DOSBase = file->af_DOSBase;
  8. #endif
  9.     LONG    current, target;
  10.     LONG    minBuf, maxBuf;
  11.     LONG    bytesArrived;
  12.     LONG    diff;
  13.     LONG    filePos;
  14.     LONG    roundTarget;
  15.     D_S( struct FileInfoBlock, fib );
  16.  
  17.     bytesArrived = AS_WaitPacket( file );
  18.  
  19.     if( bytesArrived < 0 )
  20.     {
  21.         return( -1 );
  22.     }
  23.  
  24.     if( file->af_ReadMode )
  25.     {
  26.         /* figure out what the actual file position is */
  27.         filePos = Seek( file->af_File, 0, OFFSET_CURRENT );
  28.  
  29.         if( filePos < 0 )
  30.         {
  31.             AS_RecordSyncFailure( file );
  32.             return( -1 );
  33.         }
  34.  
  35.         /* figure out what the caller's file position is */
  36.         current = filePos - ( file->af_BytesLeft + bytesArrived ) + file->af_SeekOffset;
  37.         file->af_SeekOffset = 0;
  38.  
  39.         /* figure out the absolute offset within the file where we must seek to */
  40.         if( mode == MODE_CURRENT )
  41.         {
  42.             target = current + position;
  43.         }
  44.         else if( mode == MODE_START )
  45.         {
  46.             target = position;
  47.         }
  48.         else /* if( mode == MODE_END ) */
  49.         {
  50.             if( !ExamineFH( file->af_File, fib ) )
  51.             {
  52.                 AS_RecordSyncFailure( file );
  53.                 return( -1 );
  54.             }
  55.  
  56.             target = fib->fib_Size + position;
  57.         }
  58.  
  59.         /* MH: Here we must be able to handle two different situations:
  60.          * 1) A seek directly after having dropped both buffers, and started
  61.          *    refilling (typical case: File open).
  62.          * 2) Other seeks (typical case: A seek after some initial reading).
  63.          *
  64.          * We need to subtract with "af_Buffers[ 1 - file->af_CurrentBuf ]",
  65.          * as af_CurrentBuf refers to the *arrived* buffer, not the one we're
  66.          * currently reading from (and af_Offset points into the buffer we're
  67.          * reading from)!
  68.          *
  69.          * In case 1, there will be only one packet received. af_CurrentBuf
  70.          * will be zero, and refers to the newly arrived buffer (as it
  71.          * should). For proper behaviour in the minBuf calculation, we have
  72.          * set af_Offset to point to af_Buffers[ 1 ], when starting reading
  73.          * to empty buffers. That way wee need no special case code here.
  74.          * ReadAsync() can handle this, as af_BytesLeft == 0 then.
  75.          */
  76.  
  77.         /* figure out what range of the file is currently in our buffers */
  78.         minBuf = current - ( LONG ) ( file->af_Offset - file->af_Buffers[ 1 - file->af_CurrentBuf ] );
  79.         maxBuf = current + file->af_BytesLeft + bytesArrived;  /* WARNING: this is one too big */
  80.  
  81.         diff = target - current;
  82.  
  83.         if( ( target < minBuf ) || ( target >= maxBuf ) )
  84.         {
  85.             /* the target seek location isn't currently in our buffers, so
  86.              * move the actual file pointer to the desired location, and then
  87.              * restart the async read thing...
  88.              */
  89.  
  90.             /* this is to keep our file reading block-aligned on the device.
  91.              * block-aligned reads are generally quite a bit faster, so it is
  92.              * worth the trouble to keep things aligned
  93.              */
  94.             roundTarget = ( target / file->af_BlockSize ) * file->af_BlockSize;
  95.  
  96.             if( Seek( file->af_File, roundTarget - filePos, OFFSET_CURRENT ) < 0 )
  97.             {
  98.                 AS_RecordSyncFailure( file );
  99.                 return( -1 );
  100.             }
  101.  
  102.             AS_SendPacket( file, file->af_Buffers[ 0 ] );
  103.  
  104.             file->af_SeekOffset    = target - roundTarget;
  105.             file->af_BytesLeft    = 0;
  106.             file->af_CurrentBuf    = 0;
  107.             /* MH: We set af_Offset to the buffer not being filled, to be able to
  108.              * handle a new seek directly after this one (see above; minBuf
  109.              * calculation). If we start reading after this seek, ReadAsync()
  110.              * will handle everything correctly, as af_BytesLeft == 0.
  111.              */
  112.             file->af_Offset        = file->af_Buffers[ 1 ];
  113.         }
  114.         else if( ( target < current ) || ( diff <= file->af_BytesLeft ) )
  115.         {
  116.             /* one of the two following things is true:
  117.              *
  118.              * 1. The target seek location is within the current read buffer,
  119.              * but before the current location within the buffer. Move back
  120.              * within the buffer and pretend we never got the pending packet,
  121.              * just to make life easier, and faster, in the read routine.
  122.              *
  123.              * 2. The target seek location is ahead within the current
  124.              * read buffer. Advance to that location. As above, pretend to
  125.              * have never received the pending packet.
  126.              */
  127.  
  128.             AS_RequeuePacket( file );
  129.  
  130.             file->af_BytesLeft    -= diff;
  131.             file->af_Offset        += diff;
  132.         }
  133.         else
  134.         {
  135.             /* at this point, we know the target seek location is within
  136.              * the buffer filled in by the packet that we just received
  137.              * at the start of this function. Throw away all the bytes in the
  138.              * current buffer, send a packet out to get the async thing going
  139.              * again, readjust buffer pointers to the seek location, and return
  140.              * with a grin on your face... :-)
  141.              */
  142.  
  143.             /* MH: Don't read to the buffer we just got, but the other one! */
  144.             AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] );
  145.  
  146.             /* Account for bytes left in buffer we drop */
  147.             diff -= file->af_BytesLeft;
  148.  
  149.             /* Set the offset into the current (newly arrived) buffer. */
  150.             file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff;
  151.             file->af_BytesLeft = bytesArrived - diff;
  152.  
  153.             /* MH: This "buffer switching" is important to do. It wasn't done!
  154.              * This explains the errors one could encounter now and then.
  155.              * The AS_SendPacket() call above is not the cause, and *is* correct.
  156.              */
  157.             file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  158.         }
  159.     }
  160.     else
  161.     {
  162.         /* flush the buffers */
  163.         if( file->af_BufferSize > file->af_BytesLeft )
  164.         {
  165.             if( Write(
  166.                 file->af_File,
  167.                 file->af_Buffers[ file->af_CurrentBuf ],
  168.                 file->af_BufferSize - file->af_BytesLeft ) < 0 )
  169.             {
  170.                 AS_RecordSyncFailure( file );
  171.                 return( -1 );
  172.             }
  173.         }
  174.  
  175.         /* this will unfortunately generally result in non block-aligned file
  176.          * access. We could be sneaky and try to resync our file pos at a
  177.          * later time, but we won't bother. Seeking in write-only files is
  178.          * relatively rare (except when writing IFF files with unknown chunk
  179.          * sizes, where the chunk size has to be written after the chunk data)
  180.          */
  181.  
  182.         /* MH: Ideas on how to improve the above (not tested, since I don't need
  183.          * the SeekAsync for writing in any of my programs at the moment! ;):
  184.          *
  185.          * Add a new field to the AsyncFile struct. af_WriteOffset or something like
  186.          * that (af_SeekOffset can probably be used). Like in the read case, we
  187.          * calculate a roundTarget, but we don't seek to that (but rather to the
  188.          * "absolute" position), and save the difference in the struct. af_BytesLeft
  189.          * and af_Offset are adjusted to point into the "middle" of the buffer,
  190.          * where the write will occur. Writes then needs some minor changes:
  191.          * Instead of simply writing the buffer from the start, we add the offset
  192.          * (saved above) to the buffer base, and write the partial buffer. The
  193.          * offset is then cleared. Voila: The file still block-aligned, at the price
  194.          * of some non-optimal buffer usage.
  195.          *
  196.          * Problem: As it is now, Arg3 in the packet is always set to the buffer size.
  197.          * With the above fix, this would have to be updated for each SendPacket (i.e.
  198.          * a new argument would be needed).
  199.          */
  200.  
  201.         current = Seek( file->af_File, position, mode );
  202.  
  203.         if( current < 0 )
  204.         {
  205.             AS_RecordSyncFailure( file );
  206.             return( -1 );
  207.         }
  208.  
  209.         file->af_BytesLeft    = file->af_BufferSize;
  210.         file->af_CurrentBuf    = 0;
  211.         file->af_Offset        = file->af_Buffers[ 0 ];
  212.     }
  213.  
  214.     return( current );
  215. }
  216.