home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / biblioteki / c_library / asyncio / source / seekasync.c < prev    next >
C/C++ Source or Header  |  1977-12-31  |  8KB  |  232 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.         /* We can't clear af_SeekOffset here. If another seek is done directly
  39.          * after this one, it would mean that we will both return and start
  40.          * 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.             if( Seek( file->af_File, roundTarget - filePos, OFFSET_CURRENT ) < 0 )
  102.             {
  103.                 AS_RecordSyncFailure( file );
  104.                 return( -1 );
  105.             }
  106.  
  107.             AS_SendPacket( file, file->af_Buffers[ 0 ] );
  108.  
  109.             file->af_SeekOffset    = target - roundTarget;
  110.             file->af_BytesLeft    = 0;
  111.             file->af_CurrentBuf    = 0;
  112.             /* MH: We set af_Offset to the buffer not being filled, to be able to
  113.              * handle a new seek directly after this one (see above; minBuf
  114.              * calculation). If we start reading after this seek, ReadAsync()
  115.              * will handle everything correctly, as af_BytesLeft == 0.
  116.              */
  117.             file->af_Offset        = file->af_Buffers[ 1 ];
  118.         }
  119.         else if( ( target < current ) || ( diff <= file->af_BytesLeft ) )
  120.         {
  121.             /* one of the two following things is true:
  122.              *
  123.              * 1. The target seek location is within the current read buffer,
  124.              * but before the current location within the buffer. Move back
  125.              * within the buffer and pretend we never got the pending packet,
  126.              * just to make life easier, and faster, in the read routine.
  127.              *
  128.              * 2. The target seek location is ahead within the current
  129.              * read buffer. Advance to that location. As above, pretend to
  130.              * have never received the pending packet.
  131.              */
  132.  
  133.             AS_RequeuePacket( file );
  134.  
  135.             file->af_BytesLeft    -= diff;
  136.             file->af_Offset        += diff;
  137.  
  138.             /* MH: We don't need to clear the seek offset here, since
  139.              * if we get here, we must have read some data from the current
  140.              * buffer, and af_SeekOffset will be zero then (done by
  141.              * ReadAsync()).
  142.              */
  143.         }
  144.         else
  145.         {
  146.             /* at this point, we know the target seek location is within
  147.              * the buffer filled in by the packet that we just received
  148.              * at the start of this function. Throw away all the bytes in the
  149.              * current buffer, send a packet out to get the async thing going
  150.              * again, readjust buffer pointers to the seek location, and return
  151.              * with a grin on your face... :-)
  152.              */
  153.  
  154.             /* MH: Don't read to the buffer we just got, but the other one! */
  155.             AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] );
  156.  
  157.             /* MH: Account for bytes left in buffer we drop *and* the af_SeekOffset.
  158.              */
  159.             diff -= file->af_BytesLeft - file->af_SeekOffset;
  160.  
  161.             /* MH: Set the offset into the current (newly arrived) buffer */
  162.             file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff;
  163.             file->af_BytesLeft = bytesArrived - diff;
  164.  
  165.             /* MH: We need to clear the seek offset here, since we can't do it above.
  166.              */
  167.             file->af_SeekOffset = 0;
  168.  
  169.             /* MH: This "buffer switching" is important to do. It wasn't done!
  170.              * This explains the errors one could encounter now and then.
  171.              * The AS_SendPacket() call above is not the cause, and *is* correct.
  172.              */
  173.             file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  174.         }
  175.     }
  176.     else
  177.     {
  178.         /* flush the buffers */
  179.         if( file->af_BufferSize > file->af_BytesLeft )
  180.         {
  181.             if( Write(
  182.                 file->af_File,
  183.                 file->af_Buffers[ file->af_CurrentBuf ],
  184.                 file->af_BufferSize - file->af_BytesLeft ) < 0 )
  185.             {
  186.                 AS_RecordSyncFailure( file );
  187.                 return( -1 );
  188.             }
  189.         }
  190.  
  191.         /* this will unfortunately generally result in non block-aligned file
  192.          * access. We could be sneaky and try to resync our file pos at a
  193.          * later time, but we won't bother. Seeking in write-only files is
  194.          * relatively rare (except when writing IFF files with unknown chunk
  195.          * sizes, where the chunk size has to be written after the chunk data)
  196.          */
  197.  
  198.         /* MH: Ideas on how to improve the above (not tested, since I don't need
  199.          * the SeekAsync for writing in any of my programs at the moment! ;):
  200.          *
  201.          * Add a new field to the AsyncFile struct. af_WriteOffset or something like
  202.          * that (af_SeekOffset can probably be used). Like in the read case, we
  203.          * calculate a roundTarget, but we don't seek to that (but rather to the
  204.          * "absolute" position), and save the difference in the struct. af_BytesLeft
  205.          * and af_Offset are adjusted to point into the "middle" of the buffer,
  206.          * where the write will occur. Writes then needs some minor changes:
  207.          * Instead of simply writing the buffer from the start, we add the offset
  208.          * (saved above) to the buffer base, and write the partial buffer. The
  209.          * offset is then cleared. Voila: The file still block-aligned, at the price
  210.          * of some non-optimal buffer usage.
  211.          *
  212.          * Problem: As it is now, Arg3 in the packet is always set to the buffer size.
  213.          * With the above fix, this would have to be updated for each SendPacket (i.e.
  214.          * a new argument would be needed).
  215.          */
  216.  
  217.         current = Seek( file->af_File, position, mode );
  218.  
  219.         if( current < 0 )
  220.         {
  221.             AS_RecordSyncFailure( file );
  222.             return( -1 );
  223.         }
  224.  
  225.         file->af_BytesLeft    = file->af_BufferSize;
  226.         file->af_CurrentBuf    = 0;
  227.         file->af_Offset        = file->af_Buffers[ 0 ];
  228.     }
  229.  
  230.     return( current );
  231. }
  232.