dovecot-2.2: iostream-temp: Avoid copying data if IOSTREAM_TEMP_...

dovecot at dovecot.org dovecot at dovecot.org
Sun Feb 3 21:53:48 EET 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/50d43f04511b
changeset: 15721:50d43f04511b
user:      Timo Sirainen <tss at iki.fi>
date:      Sun Feb 03 21:53:24 2013 +0200
description:
iostream-temp: Avoid copying data if IOSTREAM_TEMP_FLAG_TRY_FD_DUP is set.

diffstat:

 src/lib/iostream-temp.c |  109 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/lib/iostream-temp.h |   10 +++-
 2 files changed, 114 insertions(+), 5 deletions(-)

diffs (179 lines):

diff -r 42e152c01ace -r 50d43f04511b src/lib/iostream-temp.c
--- a/src/lib/iostream-temp.c	Sun Feb 03 21:52:48 2013 +0200
+++ b/src/lib/iostream-temp.c	Sun Feb 03 21:53:24 2013 +0200
@@ -5,7 +5,7 @@
 #include "str.h"
 #include "safe-mkstemp.h"
 #include "write-full.h"
-#include "istream.h"
+#include "istream-private.h"
 #include "ostream-private.h"
 #include "iostream-temp.h"
 
@@ -15,7 +15,13 @@
 
 struct temp_ostream {
 	struct ostream_private ostream;
+
 	char *temp_path_prefix;
+	enum iostream_temp_flags flags;
+
+	struct istream *dupstream;
+	uoff_t dupstream_offset, dupstream_start_offset;
+
 	buffer_t *buf;
 	int fd;
 	bool fd_tried;
@@ -87,6 +93,8 @@
 	ssize_t ret = 0;
 	unsigned int i;
 
+	tstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
+
 	if (tstream->fd != -1)
 		return o_stream_temp_fd_sendv(tstream, iov, iov_count);
 
@@ -105,15 +113,91 @@
 	return ret;
 }
 
-struct ostream *iostream_temp_create(const char *temp_path_prefix)
+static int o_stream_temp_dup_cancel(struct temp_ostream *tstream)
+{
+	struct istream *input;
+	uoff_t size = tstream->dupstream_offset -
+		tstream->dupstream_start_offset;
+	off_t ret;
+
+	i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset);
+
+	input = i_stream_create_limit(tstream->dupstream, size);
+	do {
+		ret = io_stream_copy(&tstream->ostream.ostream,
+				     input, IO_BLOCK_SIZE);
+	} while (input->v_offset < tstream->dupstream_offset && ret > 0);
+	if (ret < 0 && tstream->ostream.ostream.stream_errno == 0) {
+		i_assert(input->stream_errno != 0);
+		tstream->ostream.ostream.stream_errno = input->stream_errno;
+	}
+	i_stream_destroy(&input);
+	i_stream_unref(&tstream->dupstream);
+	return ret < 0 ? -1 : 0;
+}
+
+static int o_stream_temp_dup_istream(struct temp_ostream *outstream,
+				     struct istream *instream)
+{
+	uoff_t in_size;
+	off_t ret;
+
+	if (!instream->readable_fd || i_stream_get_fd(instream) == -1)
+		return 0;
+
+	if (i_stream_get_size(instream, TRUE, &in_size) <= 0) {
+		if (outstream->dupstream != NULL)
+			return o_stream_temp_dup_cancel(outstream);
+		return 0;
+	}
+
+	if (outstream->dupstream == NULL) {
+		outstream->dupstream = instream;
+		outstream->dupstream_start_offset = instream->v_offset;
+		i_stream_ref(outstream->dupstream);
+	} else {
+		if (outstream->dupstream != instream ||
+		    outstream->dupstream_offset != instream->v_offset ||
+		    outstream->dupstream_offset > in_size)
+			return o_stream_temp_dup_cancel(outstream);
+	}
+	ret = in_size - instream->v_offset;
+	i_stream_seek(instream, in_size);
+	outstream->dupstream_offset = instream->v_offset;
+	return ret;
+}
+
+static off_t o_stream_temp_send_istream(struct ostream_private *_outstream,
+					struct istream *instream)
+{
+	struct temp_ostream *outstream = (struct temp_ostream *)_outstream;
+	uoff_t orig_offset;
+	int ret;
+
+	if ((outstream->flags & IOSTREAM_TEMP_FLAG_TRY_FD_DUP) != 0) {
+		orig_offset = outstream->dupstream_offset;
+		if ((ret = o_stream_temp_dup_istream(outstream, instream)) > 0)
+			return outstream->dupstream_offset - orig_offset;
+		if (ret < 0)
+			return -1;
+		outstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
+	}
+	return io_stream_copy(&outstream->ostream.ostream,
+			      instream, IO_BLOCK_SIZE);
+}
+
+struct ostream *iostream_temp_create(const char *temp_path_prefix,
+				     enum iostream_temp_flags flags)
 {
 	struct temp_ostream *tstream;
 	struct ostream *output;
 
 	tstream = i_new(struct temp_ostream, 1);
 	tstream->ostream.sendv = o_stream_temp_sendv;
+	tstream->ostream.send_istream = o_stream_temp_send_istream;
 	tstream->ostream.iostream.close = o_stream_temp_close;
 	tstream->temp_path_prefix = i_strdup(temp_path_prefix);
+	tstream->flags = flags;
 	tstream->buf = buffer_create_dynamic(default_pool, 8192);
 	tstream->fd = -1;
 
@@ -132,9 +216,26 @@
 {
 	struct temp_ostream *tstream =
 		(struct temp_ostream *)(*output)->real_stream;
-	struct istream *input;
+	struct istream *input, *input2;
+	uoff_t abs_offset, size;
+	int fd;
 
-	if (tstream->fd != -1) {
+	if (tstream->dupstream != NULL) {
+		abs_offset = tstream->dupstream->real_stream->abs_start_offset +
+			tstream->dupstream_start_offset;
+		size = tstream->dupstream_offset -
+			tstream->dupstream_start_offset;
+		fd = dup(i_stream_get_fd(tstream->dupstream));
+		if (fd == -1)
+			input = i_stream_create_error(errno);
+		else {
+			input2 = i_stream_create_fd(fd, max_buffer_size, TRUE);
+			i_stream_seek(input2, abs_offset);
+			input = i_stream_create_limit(input2, size);
+			i_stream_unref(&input2);
+		}
+		i_stream_unref(&tstream->dupstream);
+	} else if (tstream->fd != -1) {
 		input = i_stream_create_fd(tstream->fd, max_buffer_size, TRUE);
 		tstream->fd = -1;
 	} else {
diff -r 42e152c01ace -r 50d43f04511b src/lib/iostream-temp.h
--- a/src/lib/iostream-temp.h	Sun Feb 03 21:52:48 2013 +0200
+++ b/src/lib/iostream-temp.h	Sun Feb 03 21:53:24 2013 +0200
@@ -1,9 +1,17 @@
 #ifndef IOSTREAM_TEMP_H
 #define IOSTREAM_TEMP_H
 
+enum iostream_temp_flags {
+	/* if o_stream_send_istream() is called with a readable fd, don't
+	   actually copy the input stream, just have iostream_temp_finish()
+	   return a new iostream pointing to the fd dup()ed */
+	IOSTREAM_TEMP_FLAG_TRY_FD_DUP	= 0x01
+};
+
 /* Start writing to given output stream. The data is initially written to
    memory, and later to a temporary file that is immediately unlinked. */
-struct ostream *iostream_temp_create(const char *temp_path_prefix);
+struct ostream *iostream_temp_create(const char *temp_path_prefix,
+				     enum iostream_temp_flags flags);
 /* Finished writing to stream. Return input stream for it and free the
    output stream. */
 struct istream *iostream_temp_finish(struct ostream **output,


More information about the dovecot-cvs mailing list