]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add istream.snapshot() method for referencing the current memarea
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 26 Oct 2017 12:17:04 +0000 (15:17 +0300)
committerTimo Sirainen <tss@dovecot.fi>
Wed, 1 Nov 2017 11:45:51 +0000 (13:45 +0200)
It will be used by following commits.

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

index f41370c8804533cd14b41f6b5dcc294612b8c8d1..14b57df9cee561064ea7175f0a51af275735888d 100644 (file)
@@ -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);
 
index b13c687d0a4cda81702115e6da6fa5b6805d25fa..56711082ced3f940c47e473b4337fa40c5efaabe 100644 (file)
@@ -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;