[dovecot-cvs] dovecot/src/lib istream-limit.c,NONE,1.1 Makefile.am,1.34,1.35 iostream-internal.h,1.3,1.4 istream-data.c,1.6,1.7 istream-file.c,1.13,1.14 istream-internal.h,1.4,1.5 istream-mmap.c,1.7,1.8 istream.c,1.13,1.14 istream.h,1.8,1.9 ostream-file.c,1.23,1.24

cras at procontrol.fi cras at procontrol.fi
Sun Nov 9 20:26:27 EET 2003


Update of /home/cvs/dovecot/src/lib
In directory danu:/tmp/cvs-serv3937/lib

Modified Files:
	Makefile.am iostream-internal.h istream-data.c istream-file.c 
	istream-internal.h istream-mmap.c istream.c istream.h 
	ostream-file.c 
Added Files:
	istream-limit.c 
Log Message:
istream rewrite. instead of directly setting any limits to stream, you now
have to use i_stream_create_limit() to existing stream. this should make the
istreams much easier to create and understand how they work.



--- NEW FILE: istream-limit.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "istream-internal.h"

struct limit_istream {
	struct _istream istream;

	struct istream *input;
	uoff_t v_start_offset, v_size;
};

static void _close(struct _iostream *stream __attr_unused__)
{
}

static void _destroy(struct _iostream *stream)
{
	struct limit_istream *lstream = (struct limit_istream *) stream;

	/* get to same position in parent stream */
	i_stream_seek(lstream->input, lstream->v_start_offset +
		      lstream->istream.istream.v_offset);
	i_stream_unref(lstream->input);
}

static void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
{
	struct limit_istream *lstream = (struct limit_istream *) stream;

	i_stream_set_max_buffer_size(lstream->input, max_size);
}

static void _set_blocking(struct _iostream *stream, int timeout_msecs,
			  void (*timeout_cb)(void *), void *context)
{
	struct limit_istream *lstream = (struct limit_istream *) stream;

	i_stream_set_blocking(lstream->input, timeout_msecs,
			      timeout_cb, context);
}

static ssize_t _read(struct _istream *stream)
{
	struct limit_istream *lstream = (struct limit_istream *) stream;
	uoff_t left;
	ssize_t ret;
	size_t pos;

	if (stream->istream.v_offset >= lstream->v_size)
		return -1;

	if (lstream->input->v_offset !=
	    lstream->v_start_offset + stream->istream.v_offset) {
		i_stream_seek(lstream->input,
			      lstream->v_start_offset + stream->istream.v_offset);
	}

	if (i_stream_read(lstream->input) == -2 && stream->buffer != NULL) {
		if (lstream->istream.skip == 0)
			return -2;
		stream->istream.eof = lstream->input->eof;
	}

	stream->pos -= stream->skip;
	stream->skip = 0;
	stream->buffer = i_stream_get_data(lstream->input, &pos);

	left = lstream->v_size - stream->istream.v_offset;
	if (pos > left)
		pos = left;

	ret = pos <= lstream->istream.pos ? -1 :
		(ssize_t) (pos - stream->pos);
	lstream->istream.pos = pos;
	return ret;
}

static void _seek(struct _istream *stream, uoff_t v_offset)
{
	stream->istream.stream_errno = 0;
	stream->istream.v_offset = v_offset;
	stream->skip = stream->pos = 0;
}

static uoff_t _get_size(struct _istream *stream)
{
	struct limit_istream *lstream = (struct limit_istream *) stream;

	return lstream->v_size != (uoff_t)-1 ? lstream->v_size :
		i_stream_get_size(lstream->input);
}

struct istream *i_stream_create_limit(pool_t pool, struct istream *input,
				      uoff_t v_start_offset, uoff_t v_size)
{
	struct limit_istream *lstream;

	i_stream_ref(input);

	lstream = p_new(pool, struct limit_istream, 1);
	lstream->input = input;
	lstream->v_start_offset = v_start_offset;
	lstream->v_size = v_size;

	lstream->istream.istream.v_offset =
		input->v_offset < v_start_offset ? 0 :
		input->v_offset - v_start_offset > v_size ? v_size :
		input->v_offset - v_start_offset;

	lstream->istream.iostream.close = _close;
	lstream->istream.iostream.destroy = _destroy;
	lstream->istream.iostream.set_max_buffer_size = _set_max_buffer_size;
	lstream->istream.iostream.set_blocking = _set_blocking;

	lstream->istream.read = _read;
	lstream->istream.seek = _seek;
	lstream->istream.get_size = _get_size;

	return _i_stream_create(&lstream->istream, pool, i_stream_get_fd(input),
				input->real_stream->abs_start_offset +
				v_start_offset);
}

Index: Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/lib/Makefile.am,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- Makefile.am	21 Sep 2003 16:21:37 -0000	1.34
+++ Makefile.am	9 Nov 2003 18:26:25 -0000	1.35
@@ -23,6 +23,7 @@
 	istream.c \
 	istream-data.c \
 	istream-file.c \
+	istream-limit.c \
 	istream-mmap.c \
 	ioloop.c \
 	ioloop-notify-none.c \

Index: iostream-internal.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/iostream-internal.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- iostream-internal.h	11 Jan 2003 19:55:56 -0000	1.3
+++ iostream-internal.h	9 Nov 2003 18:26:25 -0000	1.4
@@ -23,7 +23,7 @@
 			     void (*timeout_cb)(void *), void *context);
 
 #define GET_TIMEOUT_TIME(fstream) \
-        ((fstream)->timeout_msecs == 0 ? 0 : \
+        ((fstream)->timeout_msecs <= 0 ? 0 : \
 	 time(NULL) + ((fstream)->timeout_msecs / 1000))
 #define STREAM_IS_BLOCKING(fstream) \
 	((fstream)->timeout_msecs != 0)

Index: istream-data.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/istream-data.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- istream-data.c	26 Aug 2003 21:18:16 -0000	1.6
+++ istream-data.c	9 Nov 2003 18:26:25 -0000	1.7
@@ -23,8 +23,9 @@
 {
 }
 
-static ssize_t _read(struct _istream *stream __attr_unused__)
+static ssize_t _read(struct _istream *stream)
 {
+	stream->istream.eof = TRUE;
 	return -1;
 }
 
@@ -34,10 +35,9 @@
 	stream->istream.v_offset = v_offset;
 }
 
-static void _skip(struct _istream *stream, uoff_t count)
+static uoff_t _get_size(struct _istream *stream)
 {
-	stream->skip += count;
-	stream->istream.v_offset += count;
+	return stream->pos;
 }
 
 struct istream *i_stream_create_from_data(pool_t pool, const void *data,
@@ -55,8 +55,8 @@
 	stream->iostream.set_blocking = _set_blocking;
 
 	stream->read = _read;
-	stream->skip_count = _skip;
 	stream->seek = _seek;
+	stream->get_size = _get_size;
 
-	return _i_stream_create(stream, pool, -1, 0, size);
+	return _i_stream_create(stream, pool, -1, 0);
 }

Index: istream-file.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/istream-file.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- istream-file.c	5 Nov 2003 08:42:12 -0000	1.13
+++ istream-file.c	9 Nov 2003 18:26:25 -0000	1.14
@@ -106,7 +106,6 @@
 {
 	struct file_istream *fstream = (struct file_istream *) stream;
 	time_t timeout_time;
-	uoff_t read_limit;
 	size_t size;
 	ssize_t ret;
 
@@ -130,22 +129,6 @@
 	}
 
 	size = stream->buffer_size - stream->pos;
-	if (stream->istream.v_limit > 0) {
-		i_assert(stream->istream.v_limit >= stream->istream.v_offset);
-
-		read_limit = stream->istream.v_limit -
-			stream->istream.v_offset + fstream->skip_left;
-		if (read_limit <= stream->pos - stream->skip) {
-			/* virtual limit reached == EOF */
-			stream->istream.eof = TRUE;
-			return -1;
-		}
-
-		read_limit -= stream->pos - stream->skip;
-		if (size > read_limit)
-			size = read_limit;
-	}
-
 	timeout_time = GET_TIMEOUT_TIME(fstream);
 
 	ret = -1;
@@ -161,7 +144,7 @@
 		if (fstream->file) {
 			ret = pread(stream->fd,
 				    stream->w_buffer + stream->pos, size,
-				    stream->istream.start_offset +
+				    stream->abs_start_offset +
 				    stream->istream.v_offset +
 				    (stream->pos - stream->skip));
 		} else {
@@ -170,7 +153,6 @@
 		}
 		if (ret == 0) {
 			/* EOF */
-			stream->istream.stream_errno = 0;
 			stream->istream.eof = TRUE;
 			return -1;
 		}
@@ -178,7 +160,6 @@
 		if (ret < 0) {
 			if (errno == ECONNRESET || errno == ETIMEDOUT) {
 				/* treat as disconnection */
-				stream->istream.stream_errno = 0;
 				stream->istream.eof = TRUE;
 				return -1;
 			}
@@ -193,6 +174,8 @@
 
 		if (ret > 0 && fstream->skip_left > 0) {
 			i_assert(!fstream->file);
+			i_assert(stream->skip == stream->pos);
+
 			if (fstream->skip_left >= (size_t)ret) {
 				fstream->skip_left -= ret;
 				ret = 0;
@@ -209,25 +192,16 @@
 	return ret;
 }
 
-static void _skip(struct _istream *stream, uoff_t count)
-{
-	struct file_istream *fstream = (struct file_istream *) stream;
-
-	i_assert(stream->skip == stream->pos);
-
-	if (!fstream->file)
-		fstream->skip_left += count;
-	stream->istream.v_offset += count;
-	stream->skip = stream->pos = 0;
-}
-
 static void _seek(struct _istream *stream, uoff_t v_offset)
 {
 	struct file_istream *fstream = (struct file_istream *) stream;
 
 	if (!fstream->file) {
-		stream->istream.stream_errno = ESPIPE;
-		return;
+		if (v_offset < stream->istream.v_offset) {
+			stream->istream.stream_errno = ESPIPE;
+			return;
+		}
+		fstream->skip_left += v_offset - stream->istream.v_offset;
 	}
 
 	stream->istream.stream_errno = 0;
@@ -235,6 +209,17 @@
 	stream->skip = stream->pos = 0;
 }
 
+static uoff_t _get_size(struct _istream *stream)
+{
+	struct file_istream *fstream = (struct file_istream *) stream;
+	struct stat st;
+
+	if (fstream->file && fstat(stream->fd, &st) == 0 && S_ISREG(st.st_mode))
+		return (uoff_t)st.st_size;
+	else
+		return (uoff_t)-1;
+}
+
 struct istream *i_stream_create_file(int fd, pool_t pool,
 				     size_t max_buffer_size, int autoclose_fd)
 {
@@ -251,12 +236,12 @@
 	fstream->istream.iostream.set_blocking = _set_blocking;
 
 	fstream->istream.read = _read;
-	fstream->istream.skip_count = _skip;
 	fstream->istream.seek = _seek;
+	fstream->istream.get_size = _get_size;
 
 	/* get size of fd if it's a file */
 	if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode))
 		fstream->file = TRUE;
 
-	return _i_stream_create(&fstream->istream, pool, fd, 0, 0);
+	return _i_stream_create(&fstream->istream, pool, fd, 0);
 }

Index: istream-internal.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/istream-internal.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- istream-internal.h	20 Mar 2003 22:31:36 -0000	1.4
+++ istream-internal.h	9 Nov 2003 18:26:25 -0000	1.5
@@ -10,8 +10,8 @@
 
 /* methods: */
 	ssize_t (*read)(struct _istream *stream);
-	void (*skip_count)(struct _istream *stream, uoff_t count);
 	void (*seek)(struct _istream *stream, uoff_t v_offset);
+	uoff_t (*get_size)(struct _istream *stream);
 
 /* data: */
 	struct istream istream;
@@ -20,11 +20,12 @@
 	const unsigned char *buffer;
 	unsigned char *w_buffer; /* may be NULL */
 	size_t buffer_size;
+	uoff_t abs_start_offset;
 
-	size_t skip, pos, high_pos;
+	size_t skip, pos;
 };
 
 struct istream *_i_stream_create(struct _istream *_buf, pool_t pool, int fd,
-				 uoff_t start_offset, uoff_t v_size);
+				 uoff_t abs_start_offset);
 
 #endif

Index: istream-mmap.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/istream-mmap.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- istream-mmap.c	5 Nov 2003 08:42:12 -0000	1.7
+++ istream-mmap.c	9 Nov 2003 18:26:25 -0000	1.8
@@ -14,6 +14,7 @@
 	void *mmap_base;
 	off_t mmap_offset;
 	size_t mmap_block_size;
+	uoff_t v_size;
 
 	unsigned int autoclose_fd:1;
 };
@@ -75,38 +76,23 @@
 	/* we never block */
 }
 
-static ssize_t io_stream_set_mmaped_pos(struct _istream *stream)
-{
-	struct mmap_istream *mstream = (struct mmap_istream *) stream;
-	uoff_t top;
-
-	i_assert((uoff_t)mstream->mmap_offset <=
-		 stream->istream.start_offset + stream->istream.v_limit);
-
-	top = stream->istream.start_offset + stream->istream.v_limit -
-		mstream->mmap_offset;
-	stream->pos = I_MIN(top, stream->buffer_size);
-
-	return stream->pos - stream->skip;
-}
-
 static ssize_t _read(struct _istream *stream)
 {
 	struct mmap_istream *mstream = (struct mmap_istream *) stream;
-	size_t aligned_skip, limit_size;
+	size_t aligned_skip;
 	uoff_t top;
 
-	if (stream->istream.start_offset + stream->istream.v_limit <=
-	    (uoff_t)mstream->mmap_offset + stream->pos) {
-		/* end of file */
-		stream->istream.stream_errno = 0;
-		stream->istream.eof = TRUE;
-		return -1;
-	}
+	stream->istream.stream_errno = 0;
 
 	if (stream->pos < stream->buffer_size) {
 		/* more bytes available without needing to mmap() */
-		return io_stream_set_mmaped_pos(stream);
+		stream->pos = stream->buffer_size;
+		return stream->pos - stream->skip;
+	}
+
+	if (stream->istream.v_offset >= mstream->v_size) {
+		stream->istream.eof = TRUE;
+		return -1;
 	}
 
 	aligned_skip = stream->skip & ~mmap_pagemask;
@@ -123,12 +109,11 @@
 			i_error("io_stream_read_mmaped(): munmap() failed: %m");
 	}
 
-	top = stream->istream.start_offset + stream->istream.v_size -
-		mstream->mmap_offset;
+	top = stream->abs_start_offset + mstream->v_size - mstream->mmap_offset;
 	stream->buffer_size = I_MIN(top, mstream->mmap_block_size);
 
 	i_assert((uoff_t)mstream->mmap_offset + stream->buffer_size <=
-		 stream->istream.start_offset + stream->istream.v_size);
+		 stream->abs_start_offset + mstream->v_size);
 
 	mstream->mmap_base = mmap(NULL, stream->buffer_size,
 				  PROT_READ, MAP_PRIVATE,
@@ -144,20 +129,14 @@
 	}
 	stream->buffer = mstream->mmap_base;
 
-	/* madvise() only if non-limited mmap()ed buffer area larger than
-	   page size */
-	limit_size = stream->istream.start_offset + stream->istream.v_limit -
-		mstream->mmap_offset;
-	if (limit_size > mmap_pagesize) {
-		if (limit_size > stream->buffer_size)
-			limit_size = stream->buffer_size;
-
-		if (madvise(mstream->mmap_base, limit_size,
+	if (stream->buffer_size > mmap_pagesize) {
+		if (madvise(mstream->mmap_base, stream->buffer_size,
 			    MADV_SEQUENTIAL) < 0)
 			i_error("mmap_istream.madvise(): %m");
 	}
 
-	return io_stream_set_mmaped_pos(stream);
+	stream->pos = stream->buffer_size;
+	return stream->pos - stream->skip;
 }
 
 static void _seek(struct _istream *stream, uoff_t v_offset)
@@ -165,7 +144,7 @@
 	struct mmap_istream *mstream = (struct mmap_istream *) stream;
 	uoff_t abs_offset;
 
-	abs_offset = stream->istream.start_offset + v_offset;
+	abs_offset = stream->abs_start_offset + v_offset;
 	if (stream->buffer_size != 0 &&
 	    (uoff_t)mstream->mmap_offset <= abs_offset &&
 	    (uoff_t)mstream->mmap_offset + stream->buffer_size > abs_offset) {
@@ -180,9 +159,11 @@
 	stream->istream.v_offset = v_offset;
 }
 
-static void _skip(struct _istream *stream, uoff_t count)
+static uoff_t _get_size(struct _istream *stream)
 {
-	_seek(stream, stream->istream.v_offset + count);
+	struct mmap_istream *mstream = (struct mmap_istream *) stream;
+
+	return mstream->v_size;
 }
 
 struct istream *i_stream_create_mmap(int fd, pool_t pool, size_t block_size,
@@ -199,10 +180,9 @@
 	}
 
 	if (v_size == 0) {
-		if (fstat(fd, &st) < 0) {
+		if (fstat(fd, &st) < 0)
 			i_error("i_stream_create_mmap(): fstat() failed: %m");
-			v_size = 0;
-		} else {
+		else {
 			v_size = st.st_size;
 			if (start_offset > v_size)
 				start_offset = v_size;
@@ -214,6 +194,7 @@
 	mstream->fd = fd;
         _set_max_buffer_size(&mstream->istream.iostream, block_size);
 	mstream->autoclose_fd = autoclose_fd;
+	mstream->v_size = v_size;
 
 	mstream->istream.iostream.close = _close;
 	mstream->istream.iostream.destroy = _destroy;
@@ -221,11 +202,10 @@
 	mstream->istream.iostream.set_blocking = _set_blocking;
 
 	mstream->istream.read = _read;
-	mstream->istream.skip_count = _skip;
 	mstream->istream.seek = _seek;
+	mstream->istream.get_size = _get_size;
 
-	istream = _i_stream_create(&mstream->istream, pool, fd,
-				   start_offset, v_size);
+	istream = _i_stream_create(&mstream->istream, pool, fd, start_offset);
 	istream->mmaped = TRUE;
 	return istream;
 }

Index: istream.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/istream.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- istream.c	5 Nov 2003 08:42:12 -0000	1.13
+++ istream.c	9 Nov 2003 18:26:25 -0000	1.14
@@ -39,56 +39,6 @@
 				timeout_cb, context);
 }
 
-void i_stream_set_start_offset(struct istream *stream, uoff_t offset)
-{
-	struct _istream *_stream = stream->real_stream;
-	off_t diff;
-
-	i_assert(stream->v_size == 0 ||
-		 offset <= stream->start_offset + stream->v_size);
-
-	if (offset == stream->start_offset)
-		return;
-
-	diff = (off_t)stream->start_offset - (off_t)offset;
-	stream->start_offset = offset;
-	stream->v_offset += diff;
-	if (stream->v_size != 0)
-		stream->v_size += diff;
-	if (stream->v_limit != 0)
-		stream->v_limit += diff;
-
-	/* reset buffer data */
-	_stream->skip = _stream->pos = _stream->high_pos = 0;
-}
-
-void i_stream_set_read_limit(struct istream *stream, uoff_t v_offset)
-{
-	struct _istream *_stream = stream->real_stream;
-	uoff_t max_pos;
-
-	i_assert(stream->v_size == 0 || v_offset <= stream->v_size);
-
-	stream->eof = FALSE;
-	if (_stream->high_pos != 0) {
-		_stream->pos = _stream->high_pos;
-		_stream->high_pos = 0;
-	}
-
-	if (v_offset == 0)
-		stream->v_limit = stream->v_size;
-	else {
-		i_assert(v_offset >= stream->v_offset);
-
-		stream->v_limit = v_offset;
-		max_pos = v_offset - stream->v_offset + _stream->skip;
-		if (_stream->pos > max_pos) {
-			_stream->high_pos = _stream->pos;
-			_stream->pos = max_pos;
-		}
-	}
-}
-
 ssize_t i_stream_read(struct istream *stream)
 {
 	struct _istream *_stream = stream->real_stream;
@@ -96,11 +46,6 @@
 	if (stream->closed)
 		return -1;
 
-	if (_stream->pos < _stream->high_pos) {
-		/* virtual limit reached */
-		return -1;
-	}
-
 	stream->eof = FALSE;
 	return _stream->read(_stream);
 }
@@ -110,44 +55,44 @@
 	struct _istream *_stream = stream->real_stream;
 	size_t data_size;
 
-	i_assert(stream->v_size == 0 ||
-		 stream->v_offset + count <= stream->v_size);
-
 	data_size = _stream->pos - _stream->skip;
 	if (count <= data_size) {
+		/* within buffer */
 		stream->v_offset += count;
 		_stream->skip += count;
 		return;
 	}
 
-	if (stream->closed)
-		return;
-
+	/* have to seek forward */
 	count -= data_size;
 	_stream->skip = _stream->pos;
 	stream->v_offset += data_size;
 
-	if (_stream->pos < _stream->high_pos) {
-		/* virtual limit reached */
-	} else {
-		_stream->skip_count(_stream, count);
-	}
+	if (stream->closed)
+		return;
+
+	_stream->seek(_stream, stream->v_offset + count);
 }
 
 void i_stream_seek(struct istream *stream, uoff_t v_offset)
 {
 	struct _istream *_stream = stream->real_stream;
 
-	i_assert(stream->v_size == 0 || v_offset <= stream->v_size);
-
 	if (stream->closed)
 		return;
 
-	stream->eof = FALSE;
-	_stream->high_pos = 0;
+	if (v_offset < stream->v_offset)
+		stream->eof = FALSE;
 	_stream->seek(_stream, v_offset);
 }
 
+uoff_t i_stream_get_size(struct istream *stream)
+{
+	struct _istream *_stream = stream->real_stream;
+
+	return _stream->get_size(_stream);
+}
+
 char *i_stream_next_line(struct istream *stream)
 {
 	struct _istream *_stream = stream->real_stream;
@@ -247,14 +192,128 @@
 }
 
 struct istream *_i_stream_create(struct _istream *_stream, pool_t pool, int fd,
-				 uoff_t start_offset, uoff_t v_size)
+				 uoff_t abs_start_offset)
 {
 	_stream->fd = fd;
-	_stream->istream.start_offset = start_offset;
-	_stream->istream.v_size = v_size;
-	_stream->istream.v_limit = v_size;
+	_stream->abs_start_offset = abs_start_offset;
 	_stream->istream.real_stream = _stream;
 
 	_io_stream_init(pool, &_stream->iostream);
 	return &_stream->istream;
 }
+
+#ifdef STREAM_TEST
+/* gcc istream.c -o teststream liblib.a -Wall -DHAVE_CONFIG_H -DSTREAM_TEST -g */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include "ostream.h"
+
+#define BUF_VALUE(offset) \
+        (((offset) % 256) ^ ((offset) / 256))
+
+static void check_buffer(const unsigned char *data, size_t size, size_t offset)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		i_assert(data[i] == BUF_VALUE(i+offset));
+}
+
+int main(void)
+{
+	struct istream *input, *l_input;
+	struct ostream *output1, *output2;
+	int i, fd1, fd2;
+	unsigned char buf[1024];
+	const unsigned char *data;
+	size_t size;
+
+	lib_init();
+
+	fd1 = open("teststream.1", O_RDWR | O_CREAT | O_TRUNC, 0600);
+	if (fd1 < 0)
+		i_fatal("open() failed: %m");
+	fd2 = open("teststream.2", O_RDWR | O_CREAT | O_TRUNC, 0600);
+	if (fd2 < 0)
+		i_fatal("open() failed: %m");
+
+	/* write initial data */
+	for (i = 0; i < sizeof(buf); i++)
+		buf[i] = BUF_VALUE(i);
+	write(fd1, buf, sizeof(buf));
+
+	/* test reading */
+	input = i_stream_create_file(fd1, default_pool, 512, FALSE);
+	i_assert(i_stream_get_size(input) == sizeof(buf));
+
+	i_assert(i_stream_read_data(input, &data, &size, 0) > 0);
+	i_assert(size == 512);
+	check_buffer(data, size, 0);
+
+	i_stream_seek(input, 256);
+	i_assert(i_stream_read_data(input, &data, &size, 0) > 0);
+	i_assert(size == 512);
+	check_buffer(data, size, 256);
+
+	i_stream_seek(input, 0);
+	i_assert(i_stream_read_data(input, &data, &size, 512) == -2);
+	i_assert(size == 512);
+	check_buffer(data, size, 0);
+
+	i_stream_skip(input, 900);
+	i_assert(i_stream_read_data(input, &data, &size, 0) > 0);
+	i_assert(size == sizeof(buf) - 900);
+	check_buffer(data, size, 900);
+
+	/* test moving data */
+	output1 = o_stream_create_file(fd1, default_pool, 512, FALSE);
+	output2 = o_stream_create_file(fd2, default_pool, 512, FALSE);
+
+	i_stream_seek(input, 1); size = sizeof(buf)-1;
+	i_assert(o_stream_send_istream(output2, input) == size);
+	o_stream_flush(output2);
+
+	lseek(fd2, 0, SEEK_SET);
+	i_assert(read(fd2, buf, sizeof(buf)) == size);
+	check_buffer(buf, size, 1);
+
+	i_stream_seek(input, 0);
+	o_stream_seek(output1, sizeof(buf));
+	i_assert(o_stream_send_istream(output1, input) == sizeof(buf));
+
+	/* test moving with limits */
+	l_input = i_stream_create_limit(default_pool, input,
+					sizeof(buf)/2, 512);
+	i_stream_seek(l_input, 0);
+	o_stream_seek(output1, 10);
+	i_assert(o_stream_send_istream(output1, l_input) == 512);
+
+	i_stream_set_max_buffer_size(input, sizeof(buf));
+
+	i_stream_seek(input, 0);
+	i_assert(i_stream_read_data(input, &data, &size, sizeof(buf)-1) > 0);
+	i_assert(size == sizeof(buf));
+	check_buffer(data, 10, 0);
+	check_buffer(data + 10, 512, sizeof(buf)/2);
+	check_buffer(data + 10 + 512,
+		     size - (10 + 512), 10 + 512);
+
+	/* reading within limits */
+	i_stream_seek(l_input, 0);
+	i_assert(i_stream_read_data(l_input, &data, &size, 511) > 0);
+	i_assert(size == 512);
+	i_assert(i_stream_read_data(l_input, &data, &size, 512) == -2);
+	i_assert(size == 512);
+	i_stream_skip(l_input, 511);
+	i_assert(i_stream_read_data(l_input, &data, &size, 0) > 0);
+	i_assert(size == 1);
+	i_stream_skip(l_input, 1);
+	i_assert(i_stream_read_data(l_input, &data, &size, 0) == -1);
+	i_assert(size == 0);
+
+	unlink("teststream.1");
+	unlink("teststream.2");
+	return 0;
+}
+#endif

Index: istream.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/istream.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- istream.h	5 Nov 2003 08:42:12 -0000	1.8
+++ istream.h	9 Nov 2003 18:26:25 -0000	1.9
@@ -2,8 +2,7 @@
 #define __ISTREAM_H
 
 struct istream {
-	uoff_t start_offset;
-	uoff_t v_offset, v_size, v_limit; /* relative to start_offset */
+	uoff_t v_offset;
 
 	int stream_errno;
 	unsigned int mmaped:1; /* be careful when copying data */
@@ -20,6 +19,8 @@
 				     int autoclose_fd);
 struct istream *i_stream_create_from_data(pool_t pool, const void *data,
 					  size_t size);
+struct istream *i_stream_create_limit(pool_t pool, struct istream *input,
+				      uoff_t v_start_offset, uoff_t v_size);
 
 /* Reference counting. References start from 1, so calling i_stream_unref()
    destroys the stream if i_stream_ref() is never used. */
@@ -36,12 +37,6 @@
 /* Change the maximum size for stream's input buffer to grow. Useful only
    for buffered streams (currently only file). */
 void i_stream_set_max_buffer_size(struct istream *stream, size_t max_size);
-/* Change the start_offset and drop all data in buffers. Doesn't do anything
-   if offset is the same as existing start_offset. */
-void i_stream_set_start_offset(struct istream *stream, uoff_t offset);
-/* Stream won't be read past specified offset. Giving 0 as offset
-   removes the limit. */
-void i_stream_set_read_limit(struct istream *stream, uoff_t v_offset);
 /* Makes reads blocking until at least one byte is read. timeout_cb is
    called if nothing is read in specified time. Setting timeout_msecs to 0
    makes it non-blocking. This call changes non-blocking state of file
@@ -58,6 +53,8 @@
 /* Seek to specified position from beginning of file. Never fails, the next
    read tells if it was successful. This works only for files. */
 void i_stream_seek(struct istream *stream, uoff_t v_offset);
+/* Returns size of the stream, or (uoff_t)-1 if unknown */
+uoff_t i_stream_get_size(struct istream *stream);
 /* Gets the next line from stream and returns it, or NULL if more data is
    needed to make a full line. NOTE: modifies the data in buffer for the \0,
    so it works only with buffered streams (currently only file). */

Index: ostream-file.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib/ostream-file.c,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -d -r1.23 -r1.24
--- ostream-file.c	5 Nov 2003 08:42:12 -0000	1.23
+++ ostream-file.c	9 Nov 2003 18:26:25 -0000	1.24
@@ -502,7 +502,8 @@
 }
 
 static off_t io_stream_sendfile(struct _ostream *outstream,
-				struct istream *instream, int in_fd)
+				struct istream *instream,
+				int in_fd, uoff_t in_size)
 {
 	struct file_ostream *foutstream = (struct file_ostream *) outstream;
 	time_t timeout_time;
@@ -536,12 +537,17 @@
 			break;
 		}
 
-		offset = instream->start_offset + v_offset;
-		send_size = instream->v_limit - v_offset;
+		offset = instream->real_stream->abs_start_offset + v_offset;
+		send_size = in_size - v_offset;
 
 		ret = safe_sendfile(foutstream->fd, in_fd, &offset,
 				    MAX_SSIZE_T(send_size));
-		if (ret < 0) {
+		if (ret <= 0) {
+			if (ret == 0) {
+				/* EOF */
+				break;
+			}
+
 			if (errno != EINTR && errno != EAGAIN) {
 				outstream->ostream.stream_errno = errno;
 				if (errno != EINVAL) {
@@ -568,7 +574,7 @@
 }
 
 static off_t io_stream_copy(struct _ostream *outstream,
-			    struct istream *instream, int overlapping)
+			    struct istream *instream, uoff_t in_size)
 {
 	struct file_ostream *foutstream = (struct file_ostream *) outstream;
 	time_t timeout_time;
@@ -576,7 +582,7 @@
 	struct iovec iov[3];
 	int iov_len;
 	const unsigned char *data;
-	size_t size, skip_size;
+	size_t size, skip_size, block_size;
 	ssize_t ret;
 	int pos;
 
@@ -587,14 +593,11 @@
 	for (pos = 0; pos < iov_len; pos++)
 		skip_size += iov[pos].iov_len;
 
-	i_assert(!overlapping || iov_len == 0);
-
         start_offset = instream->v_offset;
-	for (;;) {
-		if (overlapping)
-			i_stream_seek(instream, instream->v_offset);
-		(void)i_stream_read_data(instream, &data, &size,
-					 foutstream->optimal_block_size-1);
+	while (in_size > 0) {
+		block_size = I_MIN(foutstream->optimal_block_size, in_size);
+		(void)i_stream_read_data(instream, &data, &size, block_size-1);
+		in_size -= size;
 
 		if (size == 0) {
 			/* all sent */
@@ -605,12 +608,6 @@
 		iov[pos].iov_base = (void *) data;
 		iov[pos].iov_len = size;
 
-		if (overlapping) {
-			if (o_stream_seek(&outstream->ostream,
-					  outstream->ostream.offset) < 0)
-				return -1;
-		}
-
 		ret = o_stream_writev(foutstream, iov, iov_len);
  		if (ret < 0) {
 			/* error */
@@ -659,11 +656,11 @@
 }
 
 static off_t io_stream_copy_backwards(struct _ostream *outstream,
-				      struct istream *instream)
+				      struct istream *instream, uoff_t in_size)
 {
 	struct file_ostream *foutstream = (struct file_ostream *) outstream;
 	time_t timeout_time;
-	uoff_t in_start_offset, in_offset, out_offset;
+	uoff_t in_start_offset, in_offset, in_limit, out_offset;
 	const unsigned char *data;
 	size_t buffer_size, size, read_size;
 	ssize_t ret;
@@ -685,12 +682,8 @@
 	}
 
 	in_start_offset = instream->v_offset;
-	in_offset = instream->v_limit;
-	out_offset = outstream->ostream.offset +
-		(instream->v_limit - instream->v_offset);
-
-	i_assert(instream->v_size == 0 ||
-		 out_offset <= instream->start_offset + instream->v_size);
+	in_offset = in_limit = in_size;
+	out_offset = outstream->ostream.offset + (in_offset - in_start_offset);
 
 	while (in_offset > in_start_offset) {
 		if (in_offset - in_start_offset <= buffer_size)
@@ -701,10 +694,10 @@
 		out_offset -= read_size;
 
 		for (;;) {
-			i_assert(in_offset <= instream->v_limit);
+			i_assert(in_offset <= in_limit);
 
 			i_stream_seek(instream, in_offset);
-			read_size = instream->v_limit - in_offset;
+			read_size = in_limit - in_offset;
 
 			(void)i_stream_read_data(instream, &data, &size,
 						 read_size-1);
@@ -730,6 +723,7 @@
 				buffer_size -= read_size;
 			}
 		}
+		in_limit -= size;
 
 		if (o_stream_seek(&outstream->ostream, out_offset) < 0)
 			return -1;
@@ -750,50 +744,46 @@
 			outstream->ostream.stream_errno = EAGAIN;
 			return -1;
 		}
-
-		i_stream_set_read_limit(instream, in_offset);
 	}
 
-	return (off_t) (instream->v_limit - in_start_offset);
+	return (off_t) (in_size - in_start_offset);
 }
 
-static off_t send_istream_fd(struct _ostream *outstream,
-			     struct istream *instream, int in_fd)
+static off_t _send_istream(struct _ostream *outstream, struct istream *instream)
 {
 	struct file_ostream *foutstream = (struct file_ostream *) outstream;
-	uoff_t old_limit;
+	uoff_t in_size;
 	off_t ret;
-	int overlapping;
+	int in_fd, overlapping;
 
-	i_assert(instream->v_limit <= OFF_T_MAX);
-	i_assert(instream->v_offset <= instream->v_limit);
+	in_fd = i_stream_get_fd(instream);
+	in_size = i_stream_get_size(instream);
+	i_assert(instream->v_offset <= in_size);
 
 	outstream->ostream.stream_errno = 0;
-
-	if (instream->v_offset == instream->v_limit)
-		return 0;
-
 	if (in_fd != foutstream->fd)
 		overlapping = 0;
 	else {
 		/* copying data within same fd. we'll have to be careful with
 		   seeks and overlapping writes. */
+		if (in_size == (uoff_t)-1) {
+			outstream->ostream.stream_errno = EINVAL;
+			return -1;
+		}
+
 		ret = (off_t)outstream->ostream.offset -
-			(off_t)(instream->start_offset + instream->v_offset);
+			(off_t)(instream->real_stream->abs_start_offset +
+				instream->v_offset);
 		if (ret == 0) {
 			/* copying data over itself. we don't really
 			   need to do that, just fake it. */
-			return instream->v_limit - instream->v_offset;
+			return in_size - instream->v_offset;
 		}
 		overlapping = ret < 0 ? -1 : 1;
-
-		if (o_stream_seek(&outstream->ostream,
-				  outstream->ostream.offset) < 0)
-			return -1;
 	}
 
 	if (!foutstream->no_sendfile && in_fd != -1 && overlapping <= 0) {
-		ret = io_stream_sendfile(outstream, instream, in_fd);
+		ret = io_stream_sendfile(outstream, instream, in_fd, in_size);
 		if (ret >= 0 || outstream->ostream.stream_errno != EINVAL)
 			return ret;
 
@@ -804,36 +794,9 @@
 	}
 
 	if (overlapping <= 0)
-		return io_stream_copy(outstream, instream, overlapping);
-	else {
-		old_limit = instream->v_limit;
-		ret = io_stream_copy_backwards(outstream, instream);
-		i_stream_set_read_limit(instream, old_limit);
-		return ret;
-	}
-}
-
-static off_t _send_istream(struct _ostream *outstream, struct istream *instream)
-{
-	struct stat st;
-	int in_fd, ret;
-
-	in_fd = i_stream_get_fd(instream);
-	if (fstat(in_fd, &st) < 0) {
-		outstream->ostream.stream_errno = errno;
-		return -1;
-	}
-
-	if (instream->v_limit != 0)
-		return send_istream_fd(outstream, instream, in_fd);
-	else {
-		/* easier this way so we know exactly how much data we're
-		   moving */
-		i_stream_set_read_limit(instream, st.st_size);
-		ret = send_istream_fd(outstream, instream, in_fd);
-		i_stream_set_read_limit(instream, 0);
-		return ret;
-	}
+		return io_stream_copy(outstream, instream, in_size);
+	else
+		return io_stream_copy_backwards(outstream, instream, in_size);
 }
 
 struct ostream *
@@ -883,7 +846,7 @@
 				fstream->no_socket_cork = TRUE;
 				fstream->file = TRUE;
 
-				o_stream_set_blocking(ostream, 60000, 0, NULL);
+				o_stream_set_blocking(ostream, -1, 0, NULL);
 			}
 		}
 #ifndef HAVE_LINUX_SENDFILE



More information about the dovecot-cvs mailing list