istream-chain.c revision 59a63791d4ec70a134cb0dcbad1255d952075efe
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* how much of the previous link's stream still exists at the
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen beginning of our buffer. skipping through this should point to
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen the beginning of the current link's stream. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Siraineni_stream_chain_append_internal(struct istream_chain *chain,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)chain->stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t max_size = i_stream_get_max_buffer_size(stream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (cstream->istream.max_buffer_size < max_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen DLLIST2_APPEND(&chain->head, &chain->tail, link);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* if io_add_istream() has been added to this chain stream, notify
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen the callback that we have more data available. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid i_stream_chain_append(struct istream_chain *chain, struct istream *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_chain_append_internal(chain, stream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid i_stream_chain_append_eof(struct istream_chain *chain)
e248fe370c4047cee921a91b48edc37944ab0526Timo Siraineni_stream_chain_set_max_buffer_size(struct iostream_private *stream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen cstream->have_explicit_max_buffer_size = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_set_max_buffer_size(link->stream, max_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void i_stream_chain_destroy(struct iostream_private *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void i_stream_chain_read_next(struct chain_istream *cstream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const unsigned char *data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(link != NULL && link->stream != NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen data = i_stream_get_data(prev_input, &data_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* a) we have more streams, b) we have EOF, c) we need to wait
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for more streams */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we've already buffered some of the prev_input. continue
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen appending the rest to it. if it's already at EOF, there's
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen nothing more to append. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (cstream->istream.skip + cstream->prev_stream_left);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* the stream has now become "previous", so its contents in
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buffer are now part of prev_stream_left. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(i_stream_alloc(&cstream->istream, data_size),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_skip(prev_input, i_stream_get_data_size(prev_input));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool i_stream_chain_skip(struct chain_istream *cstream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_private *stream = &cstream->istream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bytes_skipped = stream->skip - cstream->prev_skip;
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen /* no need to worry about buffers, skip everything */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (bytes_skipped < cstream->prev_stream_left) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we're still skipping inside buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* done with the buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic ssize_t i_stream_chain_read(struct istream_private *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const unsigned char *data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen data = i_stream_get_data(link->stream, &data_size);
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen /* need to read more */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "read(%s) failed: %s",
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen /* EOF of this stream, go to next stream */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we read something */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen data = i_stream_get_data(link->stream, &data_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* nothing new read - preserve the buffer as it was */
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen /* we can point directly to the current stream's buffers */
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen /* we still have some of the previous stream left. merge the
2ef0e8ee48c9683f7bd6698798efa3328e4322d1Timo Sirainen new data with it. */
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen memcpy(i_stream_alloc(stream, new_bytes_count),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void i_stream_chain_close(struct iostream_private *stream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* seek to the correct position in parent stream in case it didn't
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen end with EOF */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct istream *i_stream_create_chain(struct istream_chain **chain_r)
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen cstream->istream.iostream.close = i_stream_chain_close;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen cstream->istream.iostream.destroy = i_stream_chain_destroy;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen cstream->istream.iostream.set_max_buffer_size =