]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add i_stream_nonseekable_try_seek()
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 5 Jun 2018 10:25:30 +0000 (13:25 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 6 Jun 2018 13:23:19 +0000 (16:23 +0300)
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.

src/lib/istream-private.h
src/lib/istream.c

index ff6003a98713c37cf4c86aeda519bb0d183cc912..ec8fb885ffbdeaee3a2e64a85612d7fe5e4472ea 100644 (file)
@@ -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);
index 3937118e173b22be448048e49598241077b306df..18111e65f9361a368af6d2f1169b7c1c47d3db0d 100644 (file)
@@ -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)
 {