Skip to content

Commit

Permalink
lib: Add i_stream_nonseekable_try_seek()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sirainen committed Jun 5, 2018
1 parent d7b5be8 commit 062afc1
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/lib/istream-private.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down
38 changes: 37 additions & 1 deletion src/lib/istream.c
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit 062afc1

Please sign in to comment.