diff --git a/doc/quoted.adoc b/doc/quoted.adoc index 5bc5639..367ab3a 100644 --- a/doc/quoted.adoc +++ b/doc/quoted.adoc @@ -140,3 +140,6 @@ for the name and form of the templates. Beman Dawes started the implementation of `quoted()` as a private detail header. Glen Fernandes updated the implementation and also made it public. + +Glen Fernandes corrected the implementation to properly account for stream +width and fill, and optimized it to write directly to the stream buffer. diff --git a/include/boost/io/quoted.hpp b/include/boost/io/quoted.hpp index 5effdd3..fc7d205 100644 --- a/include/boost/io/quoted.hpp +++ b/include/boost/io/quoted.hpp @@ -1,7 +1,7 @@ /* Copyright 2010 Beman Dawes -Copyright 2019 Glen Joseph Fernandes +Copyright 2019-2020 Glen Joseph Fernandes (glenjofe@gmail.com) Distributed under the Boost Software License, Version 1.0. @@ -10,10 +10,9 @@ Distributed under the Boost Software License, Version 1.0. #ifndef BOOST_IO_QUOTED_HPP #define BOOST_IO_QUOTED_HPP +#include +#include #include -#include -#include -#include namespace boost { namespace io { @@ -26,37 +25,113 @@ struct quoted_proxy { Char delim; }; +template +struct quoted_state { + const Char* string; + std::size_t size; + std::size_t count; +}; + +template +inline quoted_state +quoted_start(const Char* string, Char escape, Char delim) +{ + const Char* end = string; + std::size_t count = 2; + for (Char ch; (ch = *end) != 0; ++end) { + count += 1 + (ch == escape || ch == delim); + } + quoted_state state = { string, + static_cast(end - string), count }; + return state; +} + +template +inline quoted_state +quoted_start(const String* string, Char escape, Char delim) +{ + const Char* begin = string->data(); + std::size_t size = string->size(); + std::size_t count = 2; + for (const Char *it = begin, *end = begin + size; it != end; ++it) { + Char ch = *it; + count += 1 + (ch == escape || ch == delim); + } + quoted_state state = { begin, size, count }; + return state; +} + +template +inline bool +quoted_put(std::basic_streambuf& buf, const Char* string, + std::size_t size, std::size_t count, Char escape, Char delim) +{ + if (buf.sputc(delim) == Traits::eof()) { + return false; + } + if (size == count) { + if (static_cast(buf.sputn(string, size)) != size) { + return false; + } + } else { + for (const Char* end = string + size; string != end; ++string) { + Char ch = *string; + if ((ch == escape || ch == delim) && + buf.sputc(escape) == Traits::eof()) { + return false; + } + if (buf.sputc(ch) == Traits::eof()) { + return false; + } + } + } + return buf.sputc(delim) != Traits::eof(); +} + +template +inline std::basic_ostream& +quoted_out(std::basic_ostream& os, String* string, Char escape, + Char delim) +{ + typedef std::basic_ostream stream; + ostream_guard guard(os); + typename stream::sentry entry(os); + if (entry) { + quoted_state state = boost::io::detail::quoted_start(string, + escape, delim); + std::basic_streambuf& buf = *os.rdbuf(); + std::size_t width = static_cast(os.width()); + if (width <= state.count) { + if (!boost::io::detail::quoted_put(buf, state.string, state.size, + state.count, escape, delim)) { + return os; + } + } else if ((os.flags() & stream::adjustfield) == stream::left) { + if (!boost::io::detail::quoted_put(buf, state.string, state.size, + state.count, escape, delim) || + !boost::io::detail::buffer_fill(buf, os.fill(), + width - state.count)) { + return os; + } + } else if (!boost::io::detail::buffer_fill(buf, os.fill(), + width - state.count) || + !boost::io::detail::quoted_put(buf, state.string, state.size, + state.count, escape, delim)) { + return os; + } + os.width(0); + } + guard.release(); + return os; +} + template inline std::basic_ostream& operator<<(std::basic_ostream& os, const quoted_proxy& proxy) { - os.put(proxy.delim); - for (const Char* it = proxy.string; *it; ++it) { - if (*it == proxy.delim || *it == proxy.escape) { - os.put(proxy.escape); - } - os.put(*it); - } - return os.put(proxy.delim); -} - -template -inline std::basic_ostream& -quoted_output(std::basic_ostream& os, - const std::basic_string& string, Char escape, - Char delim) -{ - os.put(delim); - for (typename std::basic_string::const_iterator it = string.begin(), end = string.end(); - it != end; ++it) { - if (*it == delim || *it == escape) { - os.put(escape); - } - os.put(*it); - } - return os.put(delim); + return boost::io::detail::quoted_out(os, proxy.string, proxy.escape, + proxy.delim); } template @@ -65,7 +140,7 @@ operator<<(std::basic_ostream& os, const quoted_proxy*, Char>& proxy) { - return boost::io::detail::quoted_output(os, *proxy.string, proxy.escape, + return boost::io::detail::quoted_out(os, proxy.string, proxy.escape, proxy.delim); } @@ -74,7 +149,7 @@ inline std::basic_ostream& operator<<(std::basic_ostream& os, const quoted_proxy*, Char>& proxy) { - return boost::io::detail::quoted_output(os, *proxy.string, proxy.escape, + return boost::io::detail::quoted_out(os, proxy.string, proxy.escape, proxy.delim); } diff --git a/test/Jamfile b/test/Jamfile index addcd0d..3c5141b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -13,6 +13,7 @@ project : requirements pedantic on ; run ios_state_unit_test.cpp ; run ios_state_test.cpp ; run quoted_test.cpp ; +run quoted_fill_test.cpp ; run ostream_joiner_test.cpp ; run make_ostream_joiner_test.cpp ; run ostream_put_test.cpp ; diff --git a/test/ostream_put_test.cpp b/test/ostream_put_test.cpp index 5e8d5a0..814d46e 100644 --- a/test/ostream_put_test.cpp +++ b/test/ostream_put_test.cpp @@ -25,7 +25,7 @@ int main() { std::wostringstream os; os.width(1); - os.fill('.'); + os.fill(L'.'); os.setf(std::ios_base::left, std::ios_base::adjustfield); boost::io::ostream_put(os, L"xy", 2); BOOST_TEST(os.good()); @@ -45,7 +45,7 @@ int main() { std::wostringstream os; os.width(1); - os.fill('.'); + os.fill(L'.'); os.setf(std::ios_base::right, std::ios_base::adjustfield); boost::io::ostream_put(os, L"xy", 2); BOOST_TEST(os.good()); diff --git a/test/quoted_fill_test.cpp b/test/quoted_fill_test.cpp new file mode 100644 index 0000000..906dfe1 --- /dev/null +++ b/test/quoted_fill_test.cpp @@ -0,0 +1,136 @@ +/* +Copyright 2019-2020 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include +#include +#include + +int main() +{ + { + std::ostringstream os; + os.width(2); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + os << boost::io::quoted("xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "\"xy\""); + } + { + std::wostringstream os; + os.width(2); + os.fill(L'.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + os << boost::io::quoted(L"xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"\"xy\""); + } + { + std::ostringstream os; + os.width(2); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + os << boost::io::quoted("xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "\"xy\""); + } + { + std::wostringstream os; + os.width(2); + os.fill(L'.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + os << boost::io::quoted(L"xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"\"xy\""); + } + { + std::ostringstream os; + os.width(6); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + os << boost::io::quoted("xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "\"xy\".."); + } + { + std::wostringstream os; + os.width(6); + os.fill(L'.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + os << boost::io::quoted(L"xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"\"xy\".."); + } + { + std::ostringstream os; + os.width(6); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + os << boost::io::quoted("xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "..\"xy\""); + } + { + std::wostringstream os; + os.width(6); + os.fill(L'.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + os << boost::io::quoted(L"xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"..\"xy\""); + } + { + std::ostringstream os; + os.width(14); + os.fill('.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + os << boost::io::quoted("xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "\"xy\".........."); + } + { + std::wostringstream os; + os.width(14); + os.fill(L'.'); + os.setf(std::ios_base::left, std::ios_base::adjustfield); + os << boost::io::quoted(L"xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"\"xy\".........."); + } + { + std::ostringstream os; + os.width(14); + os.fill('.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + os << boost::io::quoted("xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == "..........\"xy\""); + } + { + std::wostringstream os; + os.width(14); + os.fill(L'.'); + os.setf(std::ios_base::right, std::ios_base::adjustfield); + os << boost::io::quoted(L"xy"); + BOOST_TEST(os.good()); + BOOST_TEST(os.width() == 0); + BOOST_TEST(os.str() == L"..........\"xy\""); + } + return boost::report_errors(); +}