Start of port of AsyncIO library.
[AROS-Contrib.git] / workbench / libs / asyncio / src / SeekAsync.c
blob6ab08f2f3174309aa74a263cbfdf7b7bc2e24871
1 #include "async.h"
4 static ULONG
5 GetFileSize( AsyncFile *file, LONG *size )
7 #ifdef ASIO_NOEXTERNALS
8 struct DosLibrary *DOSBase = file->af_DOSBase;
9 #endif
10 D_S( struct FileInfoBlock, fib );
12 if( !ExamineFH( file->af_File, fib ) )
14 AS_RecordSyncFailure( file );
15 return( FALSE );
18 *size = fib->fib_Size;
19 return( TRUE );
23 _LIBCALL LONG
24 SeekAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) LONG position, _REG( d1 ) SeekModes mode )
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;
33 bytesArrived = AS_WaitPacket( file );
35 /* MH: No packets can be pending here! */
37 if( bytesArrived < 0 )
39 /* MH: Experimental: Try to allow "resume" of seeks past EOF. */
41 if( file->af_SeekPastEOF )
43 /* MH: Restore saved values, to make resume possible */
44 bytesArrived = file->af_LastRes1;
45 file->af_BytesLeft = file->af_LastBytesLeft;
47 else
49 return( -1 );
53 if( file->af_ReadMode )
55 /* figure out what the actual file position is */
56 filePos = Seek( file->af_File, 0, OFFSET_CURRENT );
58 if( filePos < 0 )
60 AS_RecordSyncFailure( file );
61 return( -1 );
64 /* figure out what the caller's file position is */
65 current = filePos - ( file->af_BytesLeft + bytesArrived ) + file->af_SeekOffset;
67 /* MH: We can't clear af_SeekOffset here. If another seek is done
68 * directly after this one, it would mean that we will both return
69 * the wrong position, and start reading from the wrong position.
71 /* file->af_SeekOffset = 0; */
73 /* figure out the absolute offset within the file where we must seek to */
74 if( mode == MODE_CURRENT )
76 target = current + position;
78 else if( mode == MODE_START )
80 target = position;
82 else /* if( mode == MODE_END ) */
84 if( !GetFileSize( file, &fileSize ) )
86 return( -1 );
89 target = fileSize + position;
92 /* MH: Here we must be able to handle two different situations:
93 * 1) A seek directly after having dropped both buffers, and started
94 * refilling (typical case: File open).
95 * 2) Other seeks (typical case: A seek after some initial reading).
97 * We need to subtract with "af_Buffers[ 1 - file->af_CurrentBuf ]",
98 * as af_CurrentBuf refers to the *arrived* buffer, not the one we're
99 * currently reading from (and af_Offset points into the buffer we're
100 * reading from)!
102 * In case 1, there will be only one packet received. af_CurrentBuf
103 * will be zero, and refers to the newly arrived buffer (as it
104 * should). For proper behaviour in the minBuf calculation, we have
105 * set af_Offset to point to af_Buffers[ 1 ], when starting reading
106 * to empty buffers. That way wee need no special case code here.
107 * ReadAsync() can handle this, as af_BytesLeft == 0 in that case.
110 /* figure out what range of the file is currently in our buffers */
111 minBuf = current - ( LONG ) ( file->af_Offset - file->af_Buffers[ 1 - file->af_CurrentBuf ] );
112 maxBuf = current + file->af_BytesLeft + bytesArrived; /* WARNING: this is one too big */
114 diff = target - current;
116 #ifdef DO_SOME_DEBUG
117 Printf( "Target: %ld, minBuf: %ld, maxBuf: %ld, current: %ld, diff: %ld, bytesLeft: %ld\n",
118 target, minBuf, maxBuf, current, diff, file->af_BytesLeft );
119 #endif
121 if( ( target < minBuf ) || ( target >= maxBuf ) )
123 /* the target seek location isn't currently in our buffers, so
124 * move the actual file pointer to the desired location, and then
125 * restart the async read thing...
128 if( target >= maxBuf )
130 /* MH: There's a fair chance that we really tried to seek
131 * past EOF. In order to tell for sure, we need to compare
132 * the seek target with the file size. The roundTarget may
133 * be before real EOF, so the "real" Seek() call might
134 * not notice any problems.
136 if( !GetFileSize( file, &fileSize ) )
138 return( -1 );
141 if( target > fileSize )
143 /* MH: Experimental: Try to allow "resume" of
144 * seeks past EOF.
146 file->af_SeekPastEOF = TRUE;
148 SetIoErr( ERROR_SEEK_ERROR );
149 AS_RecordSyncFailure( file );
150 return( -1 );
154 /* this is to keep our file reading block-aligned on the device.
155 * block-aligned reads are generally quite a bit faster, so it is
156 * worth the trouble to keep things aligned
158 roundTarget = ( target / file->af_BlockSize ) * file->af_BlockSize;
160 if( Seek( file->af_File, roundTarget - filePos, OFFSET_CURRENT ) < 0 )
162 AS_RecordSyncFailure( file );
163 return( -1 );
166 AS_SendPacket( file, file->af_Buffers[ 0 ] );
168 file->af_SeekOffset = target - roundTarget;
169 file->af_BytesLeft = 0;
170 file->af_CurrentBuf = 0;
172 /* MH: We set af_Offset to the buffer not being filled, to be able to
173 * handle a new seek directly after this one (see above; minBuf
174 * calculation). If we start reading after this seek, ReadAsync()
175 * will handle everything correctly, as af_BytesLeft == 0.
177 file->af_Offset = file->af_Buffers[ 1 ];
179 else if( ( target < current ) || ( diff <= file->af_BytesLeft ) )
181 /* one of the two following things is true:
183 * 1. The target seek location is within the current read buffer,
184 * but before the current location within the buffer. Move back
185 * within the buffer and pretend we never got the pending packet,
186 * just to make life easier, and faster, in the read routine.
188 * 2. The target seek location is ahead within the current
189 * read buffer. Advance to that location. As above, pretend to
190 * have never received the pending packet.
193 AS_RequeuePacket( file );
195 file->af_BytesLeft -= diff;
196 file->af_Offset += diff;
198 /* MH: We don't need to clear the seek offset here, since
199 * if we get here, we must have read some data from the current
200 * buffer, and af_SeekOffset will be zero then (done by
201 * ReadAsync()).
204 /* MH: If we're recovering from seek past EOF, restore some
205 * values here.
207 if( file->af_SeekPastEOF )
209 file->af_Packet.sp_Pkt.dp_Res1 = file->af_LastRes1;
212 else
214 /* at this point, we know the target seek location is within
215 * the buffer filled in by the packet that we just received
216 * at the start of this function. Throw away all the bytes in the
217 * current buffer, send a packet out to get the async thing going
218 * again, readjust buffer pointers to the seek location, and return
219 * with a grin on your face... :-)
222 /* MH: Don't refill the buffer we just got, but the other one! */
223 AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] );
225 /* MH: Account for bytes left in buffer we drop *and* the af_SeekOffset.
227 diff -= file->af_BytesLeft - file->af_SeekOffset;
229 /* MH: Set the offset into the current (newly arrived) buffer */
230 file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff;
231 file->af_BytesLeft = bytesArrived - diff;
233 /* MH: We need to clear the seek offset here, since we can't do it above.
235 file->af_SeekOffset = 0;
237 /* MH: This "buffer switching" is important to do. It wasn't done!
238 * This explains the errors one could encounter now and then.
239 * The AS_SendPacket() call above is not the cause, and *is* correct.
241 file->af_CurrentBuf = 1 - file->af_CurrentBuf;
244 else
246 /* flush the buffers */
247 if( file->af_BufferSize > file->af_BytesLeft )
249 if( Write(
250 file->af_File,
251 file->af_Buffers[ file->af_CurrentBuf ],
252 file->af_BufferSize - file->af_BytesLeft ) < 0 )
254 AS_RecordSyncFailure( file );
255 return( -1 );
259 /* this will unfortunately generally result in non block-aligned file
260 * access. We could be sneaky and try to resync our file pos at a
261 * later time, but we won't bother. Seeking in write-only files is
262 * relatively rare (except when writing IFF files with unknown chunk
263 * sizes, where the chunk size has to be written after the chunk data)
266 /* MH: Ideas on how to improve the above (not tested, since I don't need
267 * the SeekAsync for writing in any of my programs at the moment! ;):
269 * Add a new field to the AsyncFile struct. af_WriteOffset or something like
270 * that (af_SeekOffset can probably be used). Like in the read case, we
271 * calculate a roundTarget, but we don't seek to that (but rather to the
272 * "absolute" position), and save the difference in the struct. af_BytesLeft
273 * and af_Offset are adjusted to point into the "middle" of the buffer,
274 * where the write will occur. Writes then needs some minor changes:
275 * Instead of simply writing the buffer from the start, we add the offset
276 * (saved above) to the buffer base, and write the partial buffer. The
277 * offset is then cleared. Voila: The file is still block-aligned, at the
278 * price of some non-optimal buffer usage.
280 * Problem: As it is now, Arg3 in the packet is always set to the buffer size.
281 * With the above fix, this would have to be updated for each SendPacket (i.e.
282 * a new argument would be needed).
285 current = Seek( file->af_File, position, mode );
287 if( current < 0 )
289 AS_RecordSyncFailure( file );
290 return( -1 );
293 file->af_BytesLeft = file->af_BufferSize;
294 file->af_CurrentBuf = 0;
295 file->af_Offset = file->af_Buffers[ 0 ];
298 if( file->af_SeekPastEOF )
300 /* MH: Clear up any error flags, and restore last Res1. */
301 file->af_SeekPastEOF = FALSE;
304 SetIoErr( 0 );
305 return( current );