From: Timo Sirainen Date: Tue, 5 Jun 2018 10:25:30 +0000 (+0300) Subject: lib: Add i_stream_nonseekable_try_seek() X-Git-Tag: 2.3.2.rc1~59 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=86536a2f7fe322da18c531a72ae0b4b3953d4a64;p=thirdparty%2Fdovecot%2Fcore.git lib: Add i_stream_nonseekable_try_seek() This can be used by istreams to more easily implement seeking backwards when it has to be done by first seeking back to offset 0 and reading from there. --- diff --git a/src/lib/istream-private.h b/src/lib/istream-private.h index a88653133d..02b35f3e8d 100644 --- a/src/lib/istream-private.h +++ b/src/lib/istream-private.h @@ -39,6 +39,9 @@ struct istream_private { size_t buffer_size, max_buffer_size, init_buffer_size; size_t skip, pos, try_alloc_limit; + /* If seeking backwards within the buffer, the next read() will + return again pos..high_pos */ + size_t high_pos; struct istream *parent; /* for filter streams */ uoff_t parent_start_offset; @@ -98,6 +101,12 @@ void i_stream_free_buffer(struct istream_private *stream); ssize_t i_stream_read_copy_from_parent(struct istream *istream); void i_stream_default_seek_nonseekable(struct istream_private *stream, uoff_t v_offset, bool mark); +/* Returns FALSE if seeking must be done by starting from the beginning. + The caller is then expected to reset the stream and call this function + again, which should work then. If TRUE is returned, the seek was either + successfully done or stream_errno is set. */ +bool i_stream_nonseekable_try_seek(struct istream_private *stream, + uoff_t v_offset); /* Default snapshot handling: use memarea if it exists, otherwise snapshot parent stream. */ diff --git a/src/lib/istream.c b/src/lib/istream.c index 4310b15743..7517b64218 100644 --- a/src/lib/istream.c +++ b/src/lib/istream.c @@ -303,7 +303,15 @@ ssize_t i_stream_read_memarea(struct istream *stream) i_stream_seek(_stream->parent, _stream->parent_expected_offset); old_size = _stream->pos - _stream->skip; - ret = _stream->read(_stream); + if (_stream->pos < _stream->high_pos) { + /* we're here because we seeked back within the read buffer. */ + ret = _stream->high_pos - _stream->pos; + _stream->pos = _stream->high_pos; + _stream->high_pos = 0; + } else { + _stream->high_pos = 0; + ret = _stream->read(_stream); + } i_assert(old_size <= _stream->pos - _stream->skip); switch (ret) { case -2: @@ -1030,6 +1038,34 @@ void i_stream_default_seek_nonseekable(struct istream_private *stream, } } +bool i_stream_nonseekable_try_seek(struct istream_private *stream, + uoff_t v_offset) +{ + uoff_t start_offset = stream->istream.v_offset - stream->skip; + + if (v_offset < start_offset) { + /* have to seek backwards */ + i_stream_seek(stream->parent, stream->parent_start_offset); + stream->parent_expected_offset = stream->parent_start_offset; + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + stream->high_pos = 0; + return FALSE; + } + + if (v_offset <= start_offset + stream->pos) { + /* seeking backwards within what's already cached */ + stream->skip = v_offset - start_offset; + stream->istream.v_offset = v_offset; + stream->high_pos = stream->pos; + stream->pos = stream->skip; + } else { + /* read forward */ + i_stream_default_seek_nonseekable(stream, v_offset, FALSE); + } + return TRUE; +} + static int i_stream_default_stat(struct istream_private *stream, bool exact) {