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.2.36.1~24 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1d57ef7d8647fcf48d35e7fc63e41cc06467e332;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 ff6003a987..ec8fb885ff 100644 --- a/src/lib/istream-private.h +++ b/src/lib/istream-private.h @@ -35,6 +35,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; @@ -71,6 +74,12 @@ void *i_stream_alloc(struct istream_private *stream, size_t size); 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); struct istream *i_stream_get_root_io(struct istream *stream); void i_stream_set_io(struct istream *stream, struct io *io); diff --git a/src/lib/istream.c b/src/lib/istream.c index 3937118e17..18111e65f9 100644 --- a/src/lib/istream.c +++ b/src/lib/istream.c @@ -171,7 +171,15 @@ ssize_t i_stream_read(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: @@ -798,6 +806,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) {