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/detail/buffer_fill.hpp b/include/boost/io/detail/buffer_fill.hpp new file mode 100644 index 0000000..b25a884 --- /dev/null +++ b/include/boost/io/detail/buffer_fill.hpp @@ -0,0 +1,39 @@ +/* +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) +*/ +#ifndef BOOST_IO_DETAIL_BUFFER_FILL_HPP +#define BOOST_IO_DETAIL_BUFFER_FILL_HPP + +#include +#include + +namespace boost { +namespace io { +namespace detail { + +template +inline bool +buffer_fill(std::basic_streambuf& buf, charT ch, + std::size_t size) +{ + charT fill[] = { ch, ch, ch, ch, ch, ch, ch, ch }; + enum { + chunk = sizeof fill / sizeof(charT) + }; + for (; size > chunk; size -= chunk) { + if (static_cast(buf.sputn(fill, chunk)) != chunk) { + return false; + } + } + return static_cast(buf.sputn(fill, size)) == size; +} + +} /* detail */ +} /* io */ +} /* boost */ + +#endif diff --git a/include/boost/io/detail/ostream_guard.hpp b/include/boost/io/detail/ostream_guard.hpp new file mode 100644 index 0000000..6999d81 --- /dev/null +++ b/include/boost/io/detail/ostream_guard.hpp @@ -0,0 +1,45 @@ +/* +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) +*/ +#ifndef BOOST_IO_DETAIL_OSTREAM_GUARD_HPP +#define BOOST_IO_DETAIL_OSTREAM_GUARD_HPP + +#include +#include + +namespace boost { +namespace io { +namespace detail { + +template +class ostream_guard { +public: + explicit ostream_guard(std::basic_ostream& os) BOOST_NOEXCEPT + : os_(&os) { } + + ~ostream_guard() BOOST_NOEXCEPT_IF(false) { + if (os_) { + os_->setstate(std::basic_ostream::badbit); + } + } + + void release() BOOST_NOEXCEPT { + os_ = 0; + } + +private: + ostream_guard(const ostream_guard&); + ostream_guard& operator=(const ostream_guard&); + + std::basic_ostream* os_; +}; + +} /* detail */ +} /* io */ +} /* boost */ + +#endif diff --git a/include/boost/io/ios_state.hpp b/include/boost/io/ios_state.hpp index 10bfd73..632a122 100644 --- a/include/boost/io/ios_state.hpp +++ b/include/boost/io/ios_state.hpp @@ -7,8 +7,8 @@ Distributed under the Boost Software License, Version 1.0. #ifndef BOOST_IO_IOS_STATE_HPP #define BOOST_IO_IOS_STATE_HPP +#include #include -#include #include #ifndef BOOST_NO_STD_LOCALE #include @@ -29,7 +29,7 @@ public: : s_save_(s) , a_save_(s.flags()) { } - ios_flags_saver(state_type& s, const aspect_type& a) + ios_flags_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.flags(a)) { } @@ -58,7 +58,7 @@ public: : s_save_(s) , a_save_(s.precision()) { } - ios_precision_saver(state_type& s, const aspect_type& a) + ios_precision_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.precision(a)) { } @@ -87,7 +87,7 @@ public: : s_save_(s) , a_save_(s.width()) { } - ios_width_saver(state_type& s, const aspect_type& a) + ios_width_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.width(a)) { } @@ -117,7 +117,7 @@ public: : s_save_(s) , a_save_(s.rdstate()) { } - basic_ios_iostate_saver(state_type& s, const aspect_type& a) + basic_ios_iostate_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.rdstate()) { s.clear(a); @@ -149,11 +149,7 @@ public: : s_save_(s) , a_save_(s.exceptions()) { } -#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) basic_ios_exception_saver(state_type& s, aspect_type a) -#else - basic_ios_exception_saver(state_type& s, const aspect_type& a) -#endif : s_save_(s) , a_save_(s.exceptions()) { s.exceptions(a); @@ -185,7 +181,7 @@ public: : s_save_(s) , a_save_(s.tie()) { } - basic_ios_tie_saver(state_type& s, const aspect_type& a) + basic_ios_tie_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.tie(a)) { } @@ -215,7 +211,7 @@ public: : s_save_(s) , a_save_(s.rdbuf()) { } - basic_ios_rdbuf_saver(state_type& s, const aspect_type& a) + basic_ios_rdbuf_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.rdbuf(a)) { } @@ -245,7 +241,7 @@ public: : s_save_(s) , a_save_(s.fill()) { } - basic_ios_fill_saver(state_type& s, const aspect_type& a) + basic_ios_fill_saver(state_type& s, aspect_type a) : s_save_(s) , a_save_(s.fill(a)) { } @@ -308,7 +304,7 @@ public: , a_save_(s.iword(i)) , i_save_(i) { } - ios_iword_saver(state_type& s, index_type i, const aspect_type& a) + ios_iword_saver(state_type& s, index_type i, aspect_type a) : s_save_(s) , a_save_(s.iword(i)) , i_save_(i) { @@ -343,7 +339,7 @@ public: , a_save_(s.pword(i)) , i_save_(i) { } - ios_pword_saver(state_type& s, index_type i, const aspect_type& a) + ios_pword_saver(state_type& s, index_type i, aspect_type a) : s_save_(s) , a_save_(s.pword(i)) , i_save_(i) { diff --git a/include/boost/io/ostream_put.hpp b/include/boost/io/ostream_put.hpp index 998ad5c..97627f8 100644 --- a/include/boost/io/ostream_put.hpp +++ b/include/boost/io/ostream_put.hpp @@ -8,62 +8,11 @@ Distributed under the Boost Software License, Version 1.0. #ifndef BOOST_IO_OSTREAM_PUT_HPP #define BOOST_IO_OSTREAM_PUT_HPP -#include -#include -#include +#include +#include namespace boost { namespace io { -namespace detail { - -template -inline std::size_t -osp_put(std::basic_streambuf& out, const charT* data, - std::size_t size) -{ - return static_cast(out.sputn(data, size)); -} - -template -inline bool -osp_fill(std::basic_streambuf& out, charT c, std::size_t size) -{ - charT fill[] = { c, c, c, c, c, c, c, c }; - enum { - chunk = sizeof fill / sizeof(charT) - }; - for (; size > chunk; size -= chunk) { - if (boost::io::detail::osp_put(out, fill, chunk) != chunk) { - return false; - } - } - return boost::io::detail::osp_put(out, fill, size) == size; -} - -template -class osp_guard { -public: - explicit osp_guard(std::basic_ostream& os) BOOST_NOEXCEPT - : os_(&os) { } - - ~osp_guard() BOOST_NOEXCEPT_IF(false) { - if (os_) { - os_->setstate(std::basic_ostream::badbit); - } - } - - void release() BOOST_NOEXCEPT { - os_ = 0; - } - -private: - osp_guard(const osp_guard&); - osp_guard& operator=(const osp_guard&); - - std::basic_ostream* os_; -}; - -} /* detail */ template inline std::basic_ostream& @@ -71,22 +20,22 @@ ostream_put(std::basic_ostream& os, const charT* data, std::size_t size) { typedef std::basic_ostream stream; - detail::osp_guard guard(os); + detail::ostream_guard guard(os); typename stream::sentry entry(os); if (entry) { - std::basic_streambuf& out = *os.rdbuf(); + std::basic_streambuf& buf = *os.rdbuf(); std::size_t width = static_cast(os.width()); if (width <= size) { - if (detail::osp_put(out, data, size) != size) { + if (static_cast(buf.sputn(data, size)) != size) { return os; } } else if ((os.flags() & stream::adjustfield) == stream::left) { - if (detail::osp_put(out, data, size) != size || - !detail::osp_fill(out, os.fill(), width - size)) { + if (static_cast(buf.sputn(data, size)) != size || + !detail::buffer_fill(buf, os.fill(), width - size)) { return os; } - } else if (!detail::osp_fill(out, os.fill(), width - size) || - detail::osp_put(out, data, size) != size) { + } else if (!detail::buffer_fill(buf, os.fill(), width - size) || + static_cast(buf.sputn(data, size)) != size) { return os; } os.width(0); 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(); +}