Mercurial > dovecot > core-2.2
changeset 22980:fed657e0b156
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.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Tue, 05 Jun 2018 13:25:30 +0300 |
parents | ace8424d6f65 |
children | acc052049f0b |
files | src/lib/istream-private.h src/lib/istream.c |
diffstat | 2 files changed, 46 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/istream-private.h Tue May 29 11:53:15 2018 +0300 +++ b/src/lib/istream-private.h Tue Jun 05 13:25:30 2018 +0300 @@ -35,6 +35,9 @@ 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 @@ 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);
--- a/src/lib/istream.c Tue May 29 11:53:15 2018 +0300 +++ b/src/lib/istream.c Tue Jun 05 13:25:30 2018 +0300 @@ -171,7 +171,15 @@ 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 @@ } } +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) {