From: Timo Sirainen Date: Thu, 26 Oct 2017 12:17:04 +0000 (+0300) Subject: lib: Add istream.snapshot() method for referencing the current memarea X-Git-Tag: 2.3.0.rc1~647 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=82ea464c113f43aaa2b2e23de334cf3081c332be;p=thirdparty%2Fdovecot%2Fcore.git lib: Add istream.snapshot() method for referencing the current memarea It will be used by following commits. --- diff --git a/src/lib/istream-private.h b/src/lib/istream-private.h index f41370c880..14b57df9ce 100644 --- a/src/lib/istream-private.h +++ b/src/lib/istream-private.h @@ -20,6 +20,9 @@ struct istream_private { int (*stat)(struct istream_private *stream, bool exact); int (*get_size)(struct istream_private *stream, bool exact, uoff_t *size_r); void (*switch_ioloop)(struct istream_private *stream); + struct istream_snapshot * + (*snapshot)(struct istream_private *stream, + struct istream_snapshot *prev_snapshot); /* data: */ struct istream istream; @@ -44,6 +47,7 @@ struct istream_private { uoff_t parent_expected_offset; struct memarea *memarea; + struct istream_snapshot *prev_snapshot; /* increased every time the stream is changed (e.g. seek, read). this way streams can check if their parent streams have been accessed behind them. */ @@ -56,6 +60,11 @@ struct istream_private { bool nonpersistent_buffers:1; }; +struct istream_snapshot { + struct istream_snapshot *prev_snapshot; + struct memarea *old_memarea; +}; + struct istream * ATTR_NOWARN_UNUSED_RESULT i_stream_create(struct istream_private *stream, struct istream *parent, int fd) ATTR_NULL(2); @@ -75,6 +84,13 @@ 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); +/* Default snapshot handling: use memarea if it exists, otherwise snapshot + parent stream. */ +struct istream_snapshot * +i_stream_default_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot); +void i_stream_snapshot_free(struct istream_snapshot **snapshot); + void i_stream_set_io(struct istream *stream, struct io *io); void i_stream_unset_io(struct istream *stream, struct io *io); diff --git a/src/lib/istream.c b/src/lib/istream.c index b13c687d0a..56711082ce 100644 --- a/src/lib/istream.c +++ b/src/lib/istream.c @@ -60,6 +60,7 @@ void i_stream_unref(struct istream **stream) if (_stream->iostream.refcount == 1) { if (_stream->line_str != NULL) str_free(&_stream->line_str); + i_stream_snapshot_free(&_stream->prev_snapshot); } if (!io_stream_unref(&(*stream)->real_stream->iostream)) { i_stream_unref(&(*stream)->real_stream->parent); @@ -180,6 +181,58 @@ static void i_stream_update(struct istream_private *stream) } } +static bool snapshot_has_memarea(struct istream_snapshot *snapshot, + struct memarea *memarea) +{ + if (snapshot->old_memarea == memarea) + return TRUE; + if (snapshot->prev_snapshot != NULL) + return snapshot_has_memarea(snapshot->prev_snapshot, memarea); + return FALSE; +} + +struct istream_snapshot * +i_stream_default_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot) +{ + struct istream_snapshot *snapshot; + + if (stream->memarea != NULL) { + if (prev_snapshot != NULL) { + if (snapshot_has_memarea(prev_snapshot, stream->memarea)) + return prev_snapshot; + } + /* This stream has a memarea. Reference it, so we can later on + rollback if needed. */ + snapshot = i_new(struct istream_snapshot, 1); + snapshot->old_memarea = stream->memarea; + snapshot->prev_snapshot = prev_snapshot; + memarea_ref(snapshot->old_memarea); + return snapshot; + } + if (stream->parent == NULL) { + i_panic("%s is missing istream.snapshot() implementation", + i_stream_get_name(&stream->istream)); + } + struct istream_private *_parent_stream = + stream->parent->real_stream; + return _parent_stream->snapshot(_parent_stream, prev_snapshot); +} + +void i_stream_snapshot_free(struct istream_snapshot **_snapshot) +{ + struct istream_snapshot *snapshot = *_snapshot; + + if (*_snapshot == NULL) + return; + *_snapshot = NULL; + + i_stream_snapshot_free(&snapshot->prev_snapshot); + if (snapshot->old_memarea != NULL) + memarea_unref(&snapshot->old_memarea); + i_free(snapshot); +} + ssize_t i_stream_read(struct istream *stream) { struct istream_private *_stream = stream->real_stream; @@ -929,6 +982,11 @@ i_stream_create(struct istream_private *_stream, struct istream *parent, int fd) _stream->fd = fd; if (parent != NULL) i_stream_init_parent(_stream, parent); + else if (_stream->memarea == NULL) { + /* The stream has no parent and no memarea yet. We'll assume + that it wants to be using memareas for the reads. */ + _stream->memarea = memarea_init_empty(); + } _stream->istream.real_stream = _stream; if (_stream->iostream.close == NULL) @@ -944,6 +1002,8 @@ i_stream_create(struct istream_private *_stream, struct istream *parent, int fd) _stream->stat = i_stream_default_stat; if (_stream->get_size == NULL) _stream->get_size = i_stream_default_get_size; + if (_stream->snapshot == NULL) + _stream->snapshot = i_stream_default_snapshot; if (_stream->iostream.set_max_buffer_size == NULL) { _stream->iostream.set_max_buffer_size = i_stream_default_set_max_buffer_size;