changeset 19639:e583f67b241e

lib: istream-concat now seeks parent streams to correct offset. All of the streams' offsets were somewhat random.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 26 Jan 2016 15:20:48 +0200
parents dee68e5ac7b1
children b6dafead7c40
files src/lib/istream-concat.c src/lib/test-istream-concat.c
diffstat 2 files changed, 53 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/istream-concat.c	Tue Jan 26 15:17:34 2016 +0200
+++ b/src/lib/istream-concat.c	Tue Jan 26 15:20:48 2016 +0200
@@ -12,15 +12,19 @@
 	uoff_t *input_size;
 
 	unsigned int cur_idx, unknown_size_idx;
-	size_t prev_stream_left, prev_skip;
+	size_t prev_stream_left, prev_stream_skip, prev_skip;
 };
 
+static void i_stream_concat_skip(struct concat_istream *cstream);
+
 static void i_stream_concat_close(struct iostream_private *stream,
 				  bool close_parent)
 {
 	struct concat_istream *cstream = (struct concat_istream *)stream;
 	unsigned int i;
 
+	(void)i_stream_concat_skip(cstream);
+
 	if (close_parent) {
 		for (i = 0; cstream->input[i] != NULL; i++)
 			i_stream_close(cstream->input[i]);
@@ -53,22 +57,31 @@
 
 static void i_stream_concat_read_next(struct concat_istream *cstream)
 {
+	struct istream *prev_input = cstream->cur_input;
 	const unsigned char *data;
 	size_t data_size, size;
 
 	i_assert(cstream->cur_input->eof);
 
+	if (cstream->prev_stream_skip != 0) {
+		i_stream_skip(cstream->input[cstream->cur_idx-1], cstream->prev_stream_skip);
+		cstream->prev_stream_skip = 0;
+	}
+
 	data = i_stream_get_data(cstream->cur_input, &data_size);
 	cstream->cur_idx++;
 	cstream->cur_input = cstream->input[cstream->cur_idx];
 	i_stream_seek(cstream->cur_input, 0);
 
 	if (cstream->prev_stream_left > 0 || cstream->istream.pos == 0) {
+		/* all the pending data is already in w_buffer */
+		cstream->prev_stream_skip = data_size;
 		cstream->prev_stream_left += data_size;
 		i_assert(cstream->prev_stream_left ==
 			 cstream->istream.pos - cstream->istream.skip);
 		return;
 	}
+	i_assert(cstream->prev_stream_skip == 0);
 
 	/* we already verified that the data size is less than the
 	   maximum buffer size */
@@ -81,6 +94,7 @@
 
 	cstream->prev_stream_left = data_size;
 	memcpy(cstream->istream.w_buffer, data, data_size);
+	i_stream_skip(prev_input, data_size);
 	cstream->istream.skip = 0;
 	cstream->istream.pos = data_size;
 }
@@ -101,6 +115,9 @@
 		bytes_skipped = 0;
 	} else {
 		/* done with the buffer */
+		i_stream_skip(cstream->input[cstream->cur_idx-1], cstream->prev_stream_skip);
+		cstream->prev_stream_skip = 0;
+
 		bytes_skipped -= cstream->prev_stream_left;
 		cstream->prev_stream_left = 0;
 	}
@@ -187,6 +204,9 @@
 		}
 		stream->buffer = stream->w_buffer;
 
+		/* we'll copy all the new input to w_buffer. if we skip over
+		   prev_stream_left bytes, the next read will switch to
+		   pointing to cur_input's data directly. */
 		if (new_bytes_count > size)
 			new_bytes_count = size;
 		memcpy(stream->w_buffer + stream->pos,
@@ -247,6 +267,7 @@
 	stream->istream.v_offset = v_offset;
 	stream->skip = stream->pos = 0;
 	cstream->prev_stream_left = 0;
+	cstream->prev_stream_skip = 0;
 	cstream->prev_skip = 0;
 
 	cstream->cur_idx = find_v_offset(cstream, &v_offset);
--- a/src/lib/test-istream-concat.c	Tue Jan 26 15:17:34 2016 +0200
+++ b/src/lib/test-istream-concat.c	Tue Jan 26 15:20:48 2016 +0200
@@ -45,10 +45,14 @@
 			test_assert((char)data[j] == input_string[(input->v_offset + j) % STREAM_BYTES]);
 		}
 	}
+	test_assert(i_stream_read(input) == -1);
+	i_stream_skip(input, i_stream_get_data_size(input));
 	i_stream_unref(&input);
 
-	for (i = 0; i < STREAM_COUNT; i++)
+	for (i = 0; i < STREAM_COUNT; i++) {
+		test_assert(i_stream_is_eof(streams[i]));
 		i_stream_unref(&streams[i]);
+	}
 }
 
 static bool test_istream_concat_random(void)
@@ -113,6 +117,30 @@
 	return !test_has_failed();
 }
 
+static void test_istream_concat_early_end(void)
+{
+	struct istream *input, *streams[2];
+
+	test_begin("istream concat early end");
+
+	streams[0] = test_istream_create("stream");
+	test_istream_set_size(streams[0], 3);
+	test_istream_set_allow_eof(streams[0], FALSE);
+	streams[1] = NULL;
+
+	input = i_stream_create_concat(streams);
+	test_assert(i_stream_read(input) == 3);
+	test_istream_set_size(streams[0], 5);
+	test_assert(i_stream_read(input) == 2);
+	i_stream_skip(input, 5);
+	i_stream_unref(&input);
+
+	test_assert(streams[0]->v_offset == 5);
+	i_stream_unref(&streams[0]);
+
+	test_end();
+}
+
 void test_istream_concat(void)
 {
 	unsigned int i;
@@ -129,4 +157,6 @@
 			i = 101; /* don't break a T_BEGIN */
 	} T_END;
 	test_end();
+
+	test_istream_concat_early_end();
 }