home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / c / AsyncIO.lha / AsyncIO / src / SeekAsync.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-03  |  8.6 KB  |  259 lines

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