home *** CD-ROM | disk | FTP | other *** search
- #include "async.h"
-
- _ASM LibCall _ARGS LONG
- SeekAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) LONG position, _REG( d1 ) SeekModes mode )
- {
- #ifdef ASIO_NOEXTERNALS
- struct DosLibrary *DOSBase = file->af_DOSBase;
- #endif
- LONG current, target;
- LONG minBuf, maxBuf;
- LONG bytesArrived;
- LONG diff;
- LONG filePos;
- LONG roundTarget;
- D_S( struct FileInfoBlock, fib );
-
- bytesArrived = AS_WaitPacket( file );
-
- if( bytesArrived < 0 )
- {
- return( -1 );
- }
-
- if( file->af_ReadMode )
- {
- /* figure out what the actual file position is */
- filePos = Seek( file->af_File, 0, OFFSET_CURRENT );
-
- if( filePos < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- /* figure out what the caller's file position is */
- current = filePos - ( file->af_BytesLeft + bytesArrived ) + file->af_SeekOffset;
-
- /* MH: We can't clear af_SeekOffset here. If another seek is done
- * directly after this one, it would mean that we will both return
- * and start reading from the wrong position.
- */
- /* file->af_SeekOffset = 0; */
-
- /* figure out the absolute offset within the file where we must seek to */
- if( mode == MODE_CURRENT )
- {
- target = current + position;
- }
- else if( mode == MODE_START )
- {
- target = position;
- }
- else /* if( mode == MODE_END ) */
- {
- if( !ExamineFH( file->af_File, fib ) )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- target = fib->fib_Size + position;
- }
-
- /* MH: Here we must be able to handle two different situations:
- * 1) A seek directly after having dropped both buffers, and started
- * refilling (typical case: File open).
- * 2) Other seeks (typical case: A seek after some initial reading).
- *
- * We need to subtract with "af_Buffers[ 1 - file->af_CurrentBuf ]",
- * as af_CurrentBuf refers to the *arrived* buffer, not the one we're
- * currently reading from (and af_Offset points into the buffer we're
- * reading from)!
- *
- * In case 1, there will be only one packet received. af_CurrentBuf
- * will be zero, and refers to the newly arrived buffer (as it
- * should). For proper behaviour in the minBuf calculation, we have
- * set af_Offset to point to af_Buffers[ 1 ], when starting reading
- * to empty buffers. That way wee need no special case code here.
- * ReadAsync() can handle this, as af_BytesLeft == 0 in that case.
- */
-
- /* figure out what range of the file is currently in our buffers */
- minBuf = current - ( LONG ) ( file->af_Offset - file->af_Buffers[ 1 - file->af_CurrentBuf ] );
- maxBuf = current + file->af_BytesLeft + bytesArrived; /* WARNING: this is one too big */
-
- diff = target - current;
-
- if( ( target < minBuf ) || ( target >= maxBuf ) )
- {
- /* the target seek location isn't currently in our buffers, so
- * move the actual file pointer to the desired location, and then
- * restart the async read thing...
- */
-
- /* this is to keep our file reading block-aligned on the device.
- * block-aligned reads are generally quite a bit faster, so it is
- * worth the trouble to keep things aligned
- */
- roundTarget = ( target / file->af_BlockSize ) * file->af_BlockSize;
-
- /* MH: Really manual code optimization. ;) */
- diff = roundTarget - filePos;
-
- /* MH: One problem that arises here is that if the seek location
- * isn't in our buffers, this might be caused by having reached
- * EOF. It that case we really should return an error here since
- * the caller attempted to seek past EOF.
- *
- * To notice this situation, we check if the Seek() done below
- * will change the current seek offset in the file. If not, then
- * we would simply reload the already loaded buffer. This
- * shouldn't help the situation one bit, and a SeekAsync()
- * following this one would fall in the same trap. Thus, we can
- * safely (?) assume that re-reading the current buffer won't
- * help a bit, and we return EOF.
- */
- if( file->af_SeekOffset == ( current - diff ) )
- {
- /* MH: Assume buffer reload won't help the
- * situation...
- */
- SetIoErr( ERROR_SEEK_ERROR );
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- if( Seek( file->af_File, diff, OFFSET_CURRENT ) < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- AS_SendPacket( file, file->af_Buffers[ 0 ] );
-
- file->af_SeekOffset = target - roundTarget;
- file->af_BytesLeft = 0;
- file->af_CurrentBuf = 0;
-
- /* MH: We set af_Offset to the buffer not being filled, to be able to
- * handle a new seek directly after this one (see above; minBuf
- * calculation). If we start reading after this seek, ReadAsync()
- * will handle everything correctly, as af_BytesLeft == 0.
- */
- file->af_Offset = file->af_Buffers[ 1 ];
- }
- else if( ( target < current ) || ( diff <= file->af_BytesLeft ) )
- {
- /* one of the two following things is true:
- *
- * 1. The target seek location is within the current read buffer,
- * but before the current location within the buffer. Move back
- * within the buffer and pretend we never got the pending packet,
- * just to make life easier, and faster, in the read routine.
- *
- * 2. The target seek location is ahead within the current
- * read buffer. Advance to that location. As above, pretend to
- * have never received the pending packet.
- */
-
- AS_RequeuePacket( file );
-
- file->af_BytesLeft -= diff;
- file->af_Offset += diff;
-
- /* MH: We don't need to clear the seek offset here, since
- * if we get here, we must have read some data from the current
- * buffer, and af_SeekOffset will be zero then (done by
- * ReadAsync()).
- */
- }
- else
- {
- /* at this point, we know the target seek location is within
- * the buffer filled in by the packet that we just received
- * at the start of this function. Throw away all the bytes in the
- * current buffer, send a packet out to get the async thing going
- * again, readjust buffer pointers to the seek location, and return
- * with a grin on your face... :-)
- */
-
- /* MH: Don't read to the buffer we just got, but the other one! */
- AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] );
-
- /* MH: Account for bytes left in buffer we drop *and* the af_SeekOffset.
- */
- diff -= file->af_BytesLeft - file->af_SeekOffset;
-
- /* MH: Set the offset into the current (newly arrived) buffer */
- file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff;
- file->af_BytesLeft = bytesArrived - diff;
-
- /* MH: We need to clear the seek offset here, since we can't do it above.
- */
- file->af_SeekOffset = 0;
-
- /* MH: This "buffer switching" is important to do. It wasn't done!
- * This explains the errors one could encounter now and then.
- * The AS_SendPacket() call above is not the cause, and *is* correct.
- */
- file->af_CurrentBuf = 1 - file->af_CurrentBuf;
- }
- }
- else
- {
- /* flush the buffers */
- if( file->af_BufferSize > file->af_BytesLeft )
- {
- if( Write(
- file->af_File,
- file->af_Buffers[ file->af_CurrentBuf ],
- file->af_BufferSize - file->af_BytesLeft ) < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
- }
-
- /* this will unfortunately generally result in non block-aligned file
- * access. We could be sneaky and try to resync our file pos at a
- * later time, but we won't bother. Seeking in write-only files is
- * relatively rare (except when writing IFF files with unknown chunk
- * sizes, where the chunk size has to be written after the chunk data)
- */
-
- /* MH: Ideas on how to improve the above (not tested, since I don't need
- * the SeekAsync for writing in any of my programs at the moment! ;):
- *
- * Add a new field to the AsyncFile struct. af_WriteOffset or something like
- * that (af_SeekOffset can probably be used). Like in the read case, we
- * calculate a roundTarget, but we don't seek to that (but rather to the
- * "absolute" position), and save the difference in the struct. af_BytesLeft
- * and af_Offset are adjusted to point into the "middle" of the buffer,
- * where the write will occur. Writes then needs some minor changes:
- * Instead of simply writing the buffer from the start, we add the offset
- * (saved above) to the buffer base, and write the partial buffer. The
- * offset is then cleared. Voila: The file still block-aligned, at the price
- * of some non-optimal buffer usage.
- *
- * Problem: As it is now, Arg3 in the packet is always set to the buffer size.
- * With the above fix, this would have to be updated for each SendPacket (i.e.
- * a new argument would be needed).
- */
-
- current = Seek( file->af_File, position, mode );
-
- if( current < 0 )
- {
- AS_RecordSyncFailure( file );
- return( -1 );
- }
-
- file->af_BytesLeft = file->af_BufferSize;
- file->af_CurrentBuf = 0;
- file->af_Offset = file->af_Buffers[ 0 ];
- }
-
- return( current );
- }
-