@@ -46,6 +46,7 @@ typedef struct _DcmIOFile {
4646 char input_buffer [BUFFER_SIZE ];
4747 int64_t bytes_in_buffer ;
4848 int64_t read_point ;
49+ int64_t offset ;
4950} DcmIOFile ;
5051
5152
@@ -154,6 +155,7 @@ static int64_t refill(DcmError **error, DcmIOFile *file)
154155
155156 file -> read_point = 0 ;
156157 file -> bytes_in_buffer = bytes_read ;
158+ file -> offset += bytes_read ;
157159
158160 return bytes_read ;
159161}
@@ -202,42 +204,62 @@ static int64_t dcm_io_seek_file(DcmError **error, DcmIO *io,
202204{
203205 DcmIOFile * file = (DcmIOFile * ) io ;
204206
205- /* We've read ahead by some number of buffered bytes, so first undo that,
206- * then do the seek from the true position .
207+ /* Translate the request to an absolute target. The logical position the
208+ * caller perceives is offset - bytes_in_buffer + read_point .
207209 */
208- int64_t new_offset ;
210+ int64_t logical_pos = file -> offset - file -> bytes_in_buffer +
211+ file -> read_point ;
212+ int64_t target ;
209213
210- int64_t bytes_ahead = file -> bytes_in_buffer - file -> read_point ;
211- if (bytes_ahead > 0 ) {
212- #ifdef _WIN32
213- new_offset = _lseeki64 (file -> fd , - bytes_ahead , SEEK_CUR );
214- #else
215- new_offset = lseek (file -> fd , - bytes_ahead , SEEK_CUR );
216- #endif
214+ switch (whence ) {
215+ case SEEK_SET :
216+ target = offset ;
217+ break ;
218+ case SEEK_CUR :
219+ target = logical_pos + offset ;
220+ break ;
221+ case SEEK_END :
222+ default :
223+ target = -1 ;
224+ break ;
225+ }
217226
218- if (new_offset < 0 ) {
219- dcm_error_set (error , DCM_ERROR_CODE_IO ,
220- "unable to seek file" ,
221- "unable to seek %s - %s" , file -> filename , strerror (errno ));
227+ /* If we know the absolute target and it falls within the
228+ * currently-buffered window (offset - bytes_in_buffer, offset), we can
229+ * skip the seek entirely.
230+ */
231+ if (target >= 0 ) {
232+ int64_t buffer_base = file -> offset - file -> bytes_in_buffer ;
233+ if (target >= buffer_base && target <= file -> offset ) {
234+ file -> read_point = target - buffer_base ;
235+ return target ;
222236 }
223237 }
224238
239+ /* For SEEK_SET / SEEK_CUR we always use SEEK_SET with the resulting
240+ * absolute target.
241+ */
242+ int64_t new_offset ;
243+ int seek_whence = (target >= 0 ) ? SEEK_SET : whence ;
244+ int64_t seek_offset = (target >= 0 ) ? target : offset ;
225245#ifdef _WIN32
226- new_offset = _lseeki64 (file -> fd , offset , whence );
246+ new_offset = _lseeki64 (file -> fd , seek_offset , seek_whence );
227247#else
228- new_offset = lseek (file -> fd , offset , whence );
248+ new_offset = lseek (file -> fd , seek_offset , seek_whence );
229249#endif
230250
231251 if (new_offset < 0 ) {
232252 dcm_error_set (error , DCM_ERROR_CODE_IO ,
233253 "unable to seek file" ,
234254 "unable to seek %s - %s" , file -> filename , strerror (errno ));
255+ return new_offset ;
235256 }
236257
237- /* Empty the buffer, since we may now be at a different position.
258+ /* Empty the buffer; the next read will refill it from the new position.
238259 */
239260 file -> bytes_in_buffer = 0 ;
240261 file -> read_point = 0 ;
262+ file -> offset = new_offset ;
241263
242264 return new_offset ;
243265}
@@ -399,4 +421,3 @@ int64_t dcm_io_seek(DcmError **error,
399421{
400422 return io -> methods -> seek (error , io , offset , whence );
401423}
402-
0 commit comments