istream-chain.c revision 2454dfa32c93c20a8522c6ed42fe057baaac9f9a
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen /* how much of the previous link's stream still exists at the
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen beginning of our buffer. skipping through this should point to
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen the beginning of the current link's stream. */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Siraineni_stream_chain_append_internal(struct istream_chain *chain,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL)
597db47e06df5ac9dbf80c336383b921fec9d373Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)chain->stream;
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen size_t max_size = i_stream_get_max_buffer_size(stream);
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen if (cstream->istream.max_buffer_size < max_size)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch DLLIST2_APPEND(&chain->head, &chain->tail, link);
d295cff1b640240cd198b9c8e963c9116ab95510Timo Sirainen /* if io_add_istream() has been added to this chain stream, notify
d295cff1b640240cd198b9c8e963c9116ab95510Timo Sirainen the callback that we have more data available. */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid i_stream_chain_append(struct istream_chain *chain, struct istream *stream)
9a5980c7bb836f69a63082f4699c30596ea4ee74Timo Sirainen i_stream_chain_append_internal(chain, stream);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid i_stream_chain_append_eof(struct istream_chain *chain)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschi_stream_chain_set_max_buffer_size(struct iostream_private *stream,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct chain_istream *cstream = (struct chain_istream *)stream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen cstream->have_explicit_max_buffer_size = TRUE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_set_max_buffer_size(link->stream, max_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void i_stream_chain_destroy(struct iostream_private *stream)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct chain_istream *cstream = (struct chain_istream *)stream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void i_stream_chain_read_next(struct chain_istream *cstream)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch const unsigned char *data;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(link != NULL && link->stream != NULL);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch data = i_stream_get_data(prev_input, &data_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link);
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen /* a) we have more streams, b) we have EOF, c) we need to wait
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen for more streams */
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen /* we've already buffered some of the prev_input. continue
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen appending the rest to it. if it's already at EOF, there's
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen nothing more to append. */
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen (cstream->istream.skip + cstream->prev_stream_left);
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen /* the stream has now become "previous", so its contents in
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen buffer are now part of prev_stream_left. */
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen memcpy(i_stream_alloc(&cstream->istream, data_size),
1a3254b83ba00315cfc47d3c6e99e837914594cfTimo Sirainen i_stream_skip(prev_input, i_stream_get_data_size(prev_input));
3ab672903a7ed98263b89180261079870c964831Timo Sirainenstatic bool i_stream_chain_skip(struct chain_istream *cstream)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct istream_private *stream = &cstream->istream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch bytes_skipped = stream->skip - cstream->prev_skip;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* no need to worry about buffers, skip everything */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else if (bytes_skipped < cstream->prev_stream_left) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* we're still skipping inside buffer */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* done with the buffer */
3ab672903a7ed98263b89180261079870c964831Timo Sirainenstatic ssize_t i_stream_chain_read(struct istream_private *stream)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen const unsigned char *data;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen data = i_stream_get_data(link->stream, &data_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* need to read more */
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen "read(%s) failed: %s",
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen /* EOF of this stream, go to next stream */
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen /* we read something */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen data = i_stream_get_data(link->stream, &data_size);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen /* we can point directly to the current stream's buffers */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen /* nothing new read */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen /* we still have some of the previous stream left. merge the
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen new data with it. */
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen memcpy(i_stream_alloc(stream, new_bytes_count),
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen ret = new_pos > stream->pos ? (ssize_t)(new_pos - stream->pos) :
3ab672903a7ed98263b89180261079870c964831Timo Sirainenstatic void i_stream_chain_close(struct iostream_private *stream,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen /* seek to the correct position in parent stream in case it didn't
3ab672903a7ed98263b89180261079870c964831Timo Sirainen end with EOF */
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct istream *i_stream_create_chain(struct istream_chain **chain_r)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen cstream->istream.iostream.close = i_stream_chain_close;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.iostream.destroy = i_stream_chain_destroy;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.iostream.set_max_buffer_size =