From 4661d7b735c4c188a68df151ec03835e298c5dfe Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 16 Jun 2005 19:26:26 +0000 Subject: [PATCH] Added 'inputStreamSocketAdapter' and 'stopSequenceFilteredInputStream'. --- src/utility/filteredStream.cpp | 29 ++++ src/utility/stream.cpp | 40 +++++- tests/utility/filteredStreamTest.cpp | 124 +++++++++++++++++ vmime/utility/filteredStream.hpp | 196 +++++++++++++++++++++++++++ vmime/utility/stream.hpp | 20 +++ 5 files changed, 407 insertions(+), 2 deletions(-) diff --git a/src/utility/filteredStream.cpp b/src/utility/filteredStream.cpp index c198c230..57e17634 100644 --- a/src/utility/filteredStream.cpp +++ b/src/utility/filteredStream.cpp @@ -228,6 +228,35 @@ void CRLFToLFFilteredOutputStream::write } +// stopSequenceFilteredInputStream <1> + +template <> +const stream::size_type stopSequenceFilteredInputStream <1>::read + (value_type* const data, const size_type count) +{ + if (eof() || m_stream.eof()) + { + m_eof = true; + return 0; + } + + const size_type read = m_stream.read(data, count); + value_type* end = data + read; + + value_type* pos = std::find(data, end, m_sequence[0]); + + if (pos == end) + { + return (read); + } + else + { + m_found = 1; + return (pos - data); + } +} + + } // utility } // vmime diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp index 3899eb52..94be1447 100644 --- a/src/utility/stream.cpp +++ b/src/utility/stream.cpp @@ -319,11 +319,11 @@ const stream::size_type inputStreamPointerAdapter::skip(const size_type count) } -// outputStreamSocketAdapter - #ifdef VMIME_HAVE_MESSAGING_FEATURES +// outputStreamSocketAdapter + outputStreamSocketAdapter::outputStreamSocketAdapter(messaging::socket& sok) : m_socket(sok) { @@ -337,6 +337,42 @@ void outputStreamSocketAdapter::write } +// inputStreamSocketAdapter + +inputStreamSocketAdapter::inputStreamSocketAdapter(messaging::socket& sok) + : m_socket(sok) +{ +} + + +const bool inputStreamSocketAdapter::eof() const +{ + // Can't know... + return false; +} + + +void inputStreamSocketAdapter::reset() +{ + // Not supported +} + + +const stream::size_type inputStreamSocketAdapter::read + (value_type* const data, const size_type count) +{ + return m_socket.receiveRaw(data, count); +} + + +const stream::size_type inputStreamSocketAdapter::skip + (const size_type /* count */) +{ + // Not supported + return 0; +} + + #endif // VMIME_HAVE_MESSAGING_FEATURES diff --git a/tests/utility/filteredStreamTest.cpp b/tests/utility/filteredStreamTest.cpp index e2faf404..0552902a 100644 --- a/tests/utility/filteredStreamTest.cpp +++ b/tests/utility/filteredStreamTest.cpp @@ -53,6 +53,9 @@ namespace const size_type read(value_type* const data, const size_type /* count */) { + if (eof()) + return 0; + const std::string chunk = m_chunks[m_index]; // Warning: 'count' should be larger than chunk length. @@ -72,6 +75,25 @@ namespace }; + const std::string readWhole(vmime::utility::inputStream& is) + { + vmime::utility::stream::value_type buffer[256]; + std::string whole; + + while (!is.eof()) + { + const vmime::utility::stream::size_type read = + is.read(buffer, sizeof(buffer)); + + whole += std::string(buffer, read); + } + + return (whole); + } + + + // dotFilteredInputStream + void testDotFilteredInputStreamHelper (const std::string& number, const std::string& expected, const std::string& c1, const std::string& c2 = "", @@ -103,6 +125,9 @@ namespace testDotFilteredInputStreamHelper("6", "foo\n.bar", "foo\n", ".", ".", "bar"); } + // dotFilteredOutputStream + // CRLFToLFFilteredOutputStream + template void testFilteredOutputStreamHelper (const std::string& number, const std::string& expected, @@ -145,6 +170,102 @@ namespace testFilteredOutputStreamHelper("6", "foo\nbar", "foo", "\r", "\n", "bar"); } + // stopSequenceFilteredInputStream + + template + void testStopSequenceFISHelper + (const std::string& number, const std::string& sequence, + const std::string& expected, const std::string& c1, + const std::string& c2 = "", const std::string& c3 = "", + const std::string& c4 = "", const std::string& c5 = "") + { + chunkInputStream cis; + cis.addChunk(c1); + if (!c2.empty()) cis.addChunk(c2); + if (!c3.empty()) cis.addChunk(c3); + if (!c4.empty()) cis.addChunk(c4); + if (!c5.empty()) cis.addChunk(c5); + + vmime::utility::stopSequenceFilteredInputStream is(cis, sequence.data()); + + assert_eq(number, expected, readWhole(is)); + } + + void testStopSequenceFilteredInputStream1() + { + testStopSequenceFISHelper <1>("1", "x", "foo", "fooxbar"); + testStopSequenceFISHelper <1>("2", "x", "foo", "foox", "bar"); + testStopSequenceFISHelper <1>("3", "x", "foo", "foo", "x", "bar"); + testStopSequenceFISHelper <1>("4", "x", "foo", "fo", "o", "x", "bar"); + testStopSequenceFISHelper <1>("5", "x", "foo", "fo", "o", "x", "b", "ar"); + + testStopSequenceFISHelper <1>("6", "x", "foobar", "fo", "o", "b", "ar"); + testStopSequenceFISHelper <1>("7", "x", "foobar", "foo", "bar"); + testStopSequenceFISHelper <1>("8", "x", "foobar", "foo", "b", "ar"); + + testStopSequenceFISHelper <1>("9", "x", "foobar", "foobar"); + testStopSequenceFISHelper <1>("10", "x", "foobar", "foobarx"); + + testStopSequenceFISHelper <1>("11", "x", "", ""); + testStopSequenceFISHelper <1>("12", "x", "", "x"); + testStopSequenceFISHelper <1>("13", "x", "", "", "x"); + } + + void testStopSequenceFilteredInputStreamN_2() + { + testStopSequenceFISHelper <2>("1", "xy", "foo", "fooxybar"); + testStopSequenceFISHelper <2>("2", "xy", "foo", "foox", "ybar"); + testStopSequenceFISHelper <2>("3", "xy", "foo", "foox", "y", "bar"); + testStopSequenceFISHelper <2>("4", "xy", "foo", "foo", "x", "ybar"); + testStopSequenceFISHelper <2>("5", "xy", "foo", "foo", "xy", "bar"); + testStopSequenceFISHelper <2>("6", "xy", "foo", "foo", "x", "y", "bar"); + + testStopSequenceFISHelper <2>("7", "xy", "fooxbar", "foox", "bar"); + testStopSequenceFISHelper <2>("8", "xy", "fooxbar", "foo", "xbar"); + testStopSequenceFISHelper <2>("9", "xy", "fooxbar", "foo", "x", "bar"); + testStopSequenceFISHelper <2>("10", "xy", "foobarx", "foo", "barx"); + + testStopSequenceFISHelper <2>("11", "xy", "foobar", "foobarxy"); + testStopSequenceFISHelper <2>("12", "xy", "foobar", "foo", "barxy"); + testStopSequenceFISHelper <2>("13", "xy", "foobar", "foo", "bar", "xy"); + + testStopSequenceFISHelper <2>("14", "xy", "", ""); + testStopSequenceFISHelper <2>("15", "xy", "x", "x"); + testStopSequenceFISHelper <2>("16", "xy", "", "xy"); + testStopSequenceFISHelper <2>("17", "xy", "", "x", "y"); + } + + void testStopSequenceFilteredInputStreamN_3() + { + testStopSequenceFISHelper <3>("1", "xyz", "foo", "fooxyzbar"); + testStopSequenceFISHelper <3>("2", "xyz", "foo", "foox", "yzbar"); + testStopSequenceFISHelper <3>("3", "xyz", "foo", "foox", "y", "zbar"); + testStopSequenceFISHelper <3>("4", "xyz", "foo", "foox", "yz", "bar"); + testStopSequenceFISHelper <3>("5", "xyz", "foo", "foo", "xyz", "bar"); + testStopSequenceFISHelper <3>("6", "xyz", "foo", "foo", "xy", "zbar"); + testStopSequenceFISHelper <3>("7", "xyz", "foo", "foo", "x", "y", "zbar"); + testStopSequenceFISHelper <3>("8", "xyz", "foo", "foo", "x", "y", "z", "bar"); + testStopSequenceFISHelper <3>("9", "xyz", "foo", "fooxy", "z", "bar"); + + testStopSequenceFISHelper <3>("10", "xyz", "fooxybar", "foox", "y", "bar"); + testStopSequenceFISHelper <3>("11", "xyz", "fooxybar", "fooxy", "bar"); + testStopSequenceFISHelper <3>("12", "xyz", "fooxybar", "fo", "ox", "y", "bar"); + testStopSequenceFISHelper <3>("13", "xyz", "fooxybar", "fo", "o", "x", "y", "bar"); + testStopSequenceFISHelper <3>("14", "xyz", "fooxybar", "foo", "x", "ybar"); + testStopSequenceFISHelper <3>("15", "xyz", "fooxybar", "foo", "xybar"); + + testStopSequenceFISHelper <3>("16", "xyz", "xfoxoxybxar", "xfoxo", "xybxar"); + testStopSequenceFISHelper <3>("17", "xyz", "xfoxoxybxarx", "xfoxo", "xybxarx"); + testStopSequenceFISHelper <3>("18", "xyz", "xfoxoxybxarxy", "xfoxo", "xybxarxy"); + + testStopSequenceFISHelper <3>("19", "xyz", "", ""); + testStopSequenceFISHelper <3>("20", "xyz", "x", "x"); + testStopSequenceFISHelper <3>("21", "xyz", "xy", "xy"); + testStopSequenceFISHelper <3>("22", "xyz", "", "xyz"); + testStopSequenceFISHelper <3>("23", "xyz", "", "x", "yz"); + testStopSequenceFISHelper <3>("24", "xyz", "", "x", "y", "z"); + } + public: filteredStreamTest() : suite("vmime::utility::filteredStream") @@ -155,6 +276,9 @@ namespace add("dotFilteredInputStream", testcase(this, "dotFilteredInputStream", &filteredStreamTest::testDotFilteredInputStream)); add("dotFilteredOutputStream", testcase(this, "dotFilteredOutputStream", &filteredStreamTest::testDotFilteredOutputStream)); add("CRLFToLFFilteredOutputStream", testcase(this, "CRLFToLFFilteredOutputStream", &filteredStreamTest::testCRLFToLFFilteredOutputStream)); + add("stopSequenceFilteredInputStream1", testcase(this, "stopSequenceFilteredInputStream1", &filteredStreamTest::testStopSequenceFilteredInputStream1)); + add("stopSequenceFilteredInputStreamN_2", testcase(this, "stopSequenceFilteredInputStreamN_2", &filteredStreamTest::testStopSequenceFilteredInputStreamN_2)); + add("stopSequenceFilteredInputStreamN_3", testcase(this, "stopSequenceFilteredInputStreamN_3", &filteredStreamTest::testStopSequenceFilteredInputStreamN_3)); suite::main().add("vmime::utility::filteredStream", this); } diff --git a/vmime/utility/filteredStream.hpp b/vmime/utility/filteredStream.hpp index 61a4a1c9..f5c546bb 100644 --- a/vmime/utility/filteredStream.hpp +++ b/vmime/utility/filteredStream.hpp @@ -141,6 +141,202 @@ private: }; +/** A filtered input stream which stops when a specified sequence + * is found (eof() method will return 'true'). + */ + +template +class stopSequenceFilteredInputStream : public filteredInputStream +{ +public: + + /** Construct a new filter for the specified input stream. + * + * @param is stream from which to read data to be filtered + */ + stopSequenceFilteredInputStream(inputStream& is, const value_type* sequence) + : m_stream(is), m_sequence(sequence), m_found(0), m_eof(false) + { + } + + inputStream& getPreviousInputStream() + { + return (m_stream); + } + + const bool eof() const + { + return (m_found == COUNT || m_eof); + } + + void reset() + { + m_found = 0; + m_stream.reset(); + } + + const size_type read(value_type* const data, const size_type count); + + const size_type skip(const size_type /* count */) + { + // Not supported + return 0; + } + +private: + + inputStream& m_stream; + + const value_type* m_sequence; + size_type m_found; + + bool m_eof; +}; + + +template <> +const stream::size_type stopSequenceFilteredInputStream <1>::read + (value_type* const data, const size_type count); + + +template +const stream::size_type stopSequenceFilteredInputStream ::read + (value_type* const data, const size_type count) +{ + // Read buffer must be at least 'COUNT' size + 1 byte + if (eof() || count <= COUNT) + return 0; + + if (m_stream.eof()) + { + if (m_found != 0) + { + const size_type found = m_found; + + for (size_type f = 0 ; f < found ; ++f) + data[f] = m_sequence[f]; + + m_found = 0; + m_eof = true; + + return (found); + } + else + { + m_eof = true; + return 0; + } + } + + size_type read = m_stream.read(data, count - COUNT); + + value_type* end = data + read; + value_type* pos = data; + + while (pos < end) + { + // Very simple case, search for the whole sequence + if (m_found == 0) + { + while (pos < end) + { + pos = std::find(pos, end, m_sequence[0]); + + if (pos == end) + return (read); + + m_found = 1; + ++pos; + + while (pos < end && m_found < COUNT && m_sequence[m_found] == *pos) + { + ++m_found; + ++pos; + } + + // Didn't found whole sequence + if (m_found != COUNT) + { + // We reached the end of the buffer + if (pos == end) + { + return (read - m_found); + } + // Common prefix but not whole sequence + else + { + m_found = 0; + } + } + // Whole sequence found + else + { + // End of stream + return (pos - data - m_found); + } + } + } + // More complex case: search for a sequence which has begun + // in a previous buffer + else + { + // Search for the end of the previously started sequence + while (pos < end && m_found < COUNT && m_sequence[m_found] == *pos) + { + ++m_found; + ++pos; + } + + if (m_found != COUNT) + { + // End of buffer + if (pos == end) + { + // No data: this buffer is a sub-sequence of the + // searched sequence + return 0; + } + // Common prefix + else + { + // We have to reinject the incomplete sequence into + // the stream data + + // -- shift right data + const size_type n = pos - data; + + value_type* newEnd = data + read + m_found - n; + value_type* oldEnd = data + read; + + for (size_type i = 0 ; i < read - n ; ++i) + { + --newEnd; + --oldEnd; + + *newEnd = *oldEnd; + } + + // -- copy the prefix just before data + for (size_type f = 0 ; f < m_found ; ++f) + data[f] = m_sequence[f]; + + read += m_found - n; + end += m_found - n; + + m_found = 0; + } + } + else + { + return 0; // no more data + } + } + } + + return read; +} + + } // utility } // vmime diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp index c9967496..bfcd6192 100644 --- a/vmime/utility/stream.hpp +++ b/vmime/utility/stream.hpp @@ -340,6 +340,26 @@ private: }; +/** An input stream that is connected to a socket. + */ + +class inputStreamSocketAdapter : public inputStream +{ +public: + + inputStreamSocketAdapter(messaging::socket& sok); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + const size_type skip(const size_type count); + +private: + + messaging::socket& m_socket; +}; + + #endif // VMIME_HAVE_MESSAGING_FEATURES