home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / biblioteki / asyncio / src / seekasync.c < prev    next >
C/C++ Source or Header  |  1997-06-08  |  9KB  |  272 lines

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