From b6ec75d12023e2ada528e64a85f8f156dcaa4624 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Mon, 8 Aug 2016 00:05:45 +0300 Subject: [PATCH] Added a max_size decorator for limiting the length of the string produced by the adopted formatter. Preliminary version. --- .../boost/log/attributes/scoped_attribute.hpp | 2 +- .../log/detail/attachable_sstream_buf.hpp | 229 ++++++-- include/boost/log/detail/code_conversion.hpp | 91 ++- include/boost/log/detail/decomposed_time.hpp | 39 +- include/boost/log/detail/format.hpp | 56 +- .../expressions/formatters/char_decorator.hpp | 6 +- .../formatters/max_size_decorator.hpp | 531 ++++++++++++++++++ include/boost/log/sources/basic_logger.hpp | 2 +- include/boost/log/sources/record_ostream.hpp | 2 +- .../boost/log/utility/formatting_ostream.hpp | 45 +- .../type_dispatch/static_type_dispatcher.hpp | 2 +- include/boost/log/utility/value_ref.hpp | 2 +- src/code_conversion.cpp | 140 +++-- src/core.cpp | 12 +- src/date_time_format_parser.cpp | 11 +- src/named_scope_format_parser.cpp | 6 +- 16 files changed, 967 insertions(+), 209 deletions(-) create mode 100644 include/boost/log/expressions/formatters/max_size_decorator.hpp diff --git a/include/boost/log/attributes/scoped_attribute.hpp b/include/boost/log/attributes/scoped_attribute.hpp index bd0e52d..0e49581 100644 --- a/include/boost/log/attributes/scoped_attribute.hpp +++ b/include/boost/log/attributes/scoped_attribute.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/log/detail/attachable_sstream_buf.hpp b/include/boost/log/detail/attachable_sstream_buf.hpp index ae536a4..dd70d0e 100644 --- a/include/boost/log/detail/attachable_sstream_buf.hpp +++ b/include/boost/log/detail/attachable_sstream_buf.hpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2016. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -16,11 +16,14 @@ #ifndef BOOST_LOG_ATTACHABLE_SSTREAM_BUF_HPP_INCLUDED_ #define BOOST_LOG_ATTACHABLE_SSTREAM_BUF_HPP_INCLUDED_ +#include #include +#include #include #include #include -#include +#include +#include #include #include @@ -58,66 +61,195 @@ public: typedef typename base_type::traits_type traits_type; //! String type typedef std::basic_string< char_type, traits_type, AllocatorT > string_type; + //! Size type + typedef typename string_type::size_type size_type; //! Int type typedef typename base_type::int_type int_type; + struct storage_state + { + //! A reference to the string that will be filled + string_type* storage; + //! Max size of the storage, in characters + size_type max_size; + //! Indicates that storage overflow happened + bool overflow; + + BOOST_CONSTEXPR storage_state() BOOST_NOEXCEPT : storage(NULL), max_size(0u), overflow(false) + { + } + }; + private: - //! A reference to the string that will be filled - string_type* m_Storage; + //! Buffer storage state + storage_state m_storage_state; //! A buffer used to temporarily store output - char_type m_Buffer[buffer_size]; + char_type m_buffer[buffer_size]; public: //! Constructor - explicit basic_ostringstreambuf() : m_Storage(0) + basic_ostringstreambuf() BOOST_NOEXCEPT { - base_type::setp(m_Buffer, m_Buffer + (sizeof(m_Buffer) / sizeof(*m_Buffer))); + base_type::setp(m_buffer, m_buffer + (sizeof(m_buffer) / sizeof(*m_buffer))); } //! Constructor - explicit basic_ostringstreambuf(string_type& storage) : m_Storage(boost::addressof(storage)) + explicit basic_ostringstreambuf(string_type& storage) BOOST_NOEXCEPT { - base_type::setp(m_Buffer, m_Buffer + (sizeof(m_Buffer) / sizeof(*m_Buffer))); + base_type::setp(m_buffer, m_buffer + (sizeof(m_buffer) / sizeof(*m_buffer))); + attach(storage); } - //! Clears the buffer to the initial state - void clear() - { - char_type* pBase = this->pbase(); - char_type* pPtr = this->pptr(); - if (pBase != pPtr) - this->pbump(static_cast< int >(pBase - pPtr)); - } + storage_state const& get_storage_state() const BOOST_NOEXCEPT { return m_storage_state; } + void set_storage_state(storage_state const& st) BOOST_NOEXCEPT { m_storage_state = st; } //! Detaches the buffer from the string void detach() { - if (m_Storage) + if (m_storage_state.storage) { this_type::sync(); - m_Storage = 0; + m_storage_state.storage = NULL; + m_storage_state.max_size = 0u; + m_storage_state.overflow = false; } } //! Attaches the buffer to another string void attach(string_type& storage) + { + attach(storage, storage.max_size()); + } + + //! Attaches the buffer to another string + void attach(string_type& storage, size_type max_size) { detach(); - m_Storage = boost::addressof(storage); + m_storage_state.storage = &storage; + this->max_size(max_size); } //! Returns a pointer to the attached string - string_type* storage() const { return m_Storage; } + string_type* storage() const BOOST_NOEXCEPT { return m_storage_state.storage; } + + //! Returns the maximum size of the storage + size_type max_size() const BOOST_NOEXCEPT { return m_storage_state.max_size; } + //! Sets the maximum size of the storage + void max_size(size_type size) + { + if (m_storage_state.storage) + { + const size_type storage_max_size = m_storage_state.storage->max_size(); + size = size > storage_max_size ? storage_max_size : size; + } + + m_storage_state.max_size = size; + ensure_max_size(); + } + //! Makes sure the storage does not exceed the max size limit. Should be called after the storage is modified externally. + void ensure_max_size() + { + if (m_storage_state.storage && m_storage_state.storage->size() > m_storage_state.max_size) + { + m_storage_state.storage->resize(m_storage_state.max_size); + m_storage_state.overflow = true; + } + } + + //! Returns true if the max size limit has been exceeded + bool storage_overflow() const BOOST_NOEXCEPT { return m_storage_state.overflow; } + //! Sets the overflow flag + void storage_overflow(bool f) BOOST_NOEXCEPT { m_storage_state.overflow = f; } + + //! Returns the size left in the storage + size_type size_left() const BOOST_NOEXCEPT + { + BOOST_ASSERT(m_storage_state.storage != NULL); + + const size_type size = m_storage_state.storage->size(); + return size < m_storage_state.max_size ? m_storage_state.max_size - size : static_cast< size_type >(0u); + } + + //! Appends a string to the storage and returns the number of written characters + size_type append(const char_type* s, size_type n) + { + if (!m_storage_state.overflow) + { + BOOST_ASSERT(m_storage_state.storage != NULL); + + size_type left = size_left(); + BOOST_LOG_ASSUME(left <= m_storage_state.storage->max_size()); + if (BOOST_LIKELY(n <= left)) + { + m_storage_state.storage->append(s, n); + return n; + } + else + { + // We have to find out where the last character that fits before the limit ends + left = length_until_boundary(s, n, left, mpl::bool_< sizeof(char_type) == 1u >()); + m_storage_state.storage->append(s, left); + m_storage_state.overflow = true; + return left; + } + } + return 0u; + } + + //! Appends the specified number of characters to the storage and returns the number of written characters + size_type append(size_type n, char_type c) + { + if (!m_storage_state.overflow) + { + BOOST_ASSERT(m_storage_state.storage != NULL); + + const size_type left = size_left(); + BOOST_LOG_ASSUME(left <= m_storage_state.storage->max_size()); + if (BOOST_LIKELY(n <= left)) + { + m_storage_state.storage->append(n, c); + return n; + } + else + { + m_storage_state.storage->append(left, c); + m_storage_state.overflow = true; + return left; + } + } + return 0u; + } + + //! Appends a character to the storage and returns the number of written characters + size_type push_back(char_type c) + { + if (!m_storage_state.overflow) + { + BOOST_ASSERT(m_storage_state.storage != NULL); + + BOOST_LOG_ASSUME(m_storage_state.max_size <= m_storage_state.storage->max_size()); + if (BOOST_LIKELY(m_storage_state.storage->size() < m_storage_state.max_size)) + { + m_storage_state.storage->push_back(c); + return 1u; + } + else + { + m_storage_state.overflow = true; + return 0u; + } + } + return 0u; + } protected: //! Puts all buffered data to the string int sync() { - BOOST_ASSERT(m_Storage != 0); char_type* pBase = this->pbase(); char_type* pPtr = this->pptr(); if (pBase != pPtr) { - m_Storage->append(pBase, pPtr); + this->append(pBase, static_cast< size_type >(pPtr - pBase)); this->pbump(static_cast< int >(pBase - pPtr)); } return 0; @@ -125,11 +257,10 @@ protected: //! Puts an unbuffered character to the string int_type overflow(int_type c) { - BOOST_ASSERT(m_Storage != 0); - basic_ostringstreambuf::sync(); + this_type::sync(); if (!traits_type::eq_int_type(c, traits_type::eof())) { - m_Storage->push_back(traits_type::to_char_type(c)); + this->push_back(traits_type::to_char_type(c)); return c; } else @@ -138,21 +269,41 @@ protected: //! Puts a character sequence to the string std::streamsize xsputn(const char_type* s, std::streamsize n) { - BOOST_ASSERT(m_Storage != 0); - basic_ostringstreambuf::sync(); - typedef typename string_type::size_type string_size_type; - const string_size_type max_storage_left = - m_Storage->max_size() - m_Storage->size(); - if (static_cast< string_size_type >(n) < max_storage_left) + this_type::sync(); + return static_cast< std::streamsize >(this->append(s, static_cast< size_type >(n))); + } + + //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size + size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, mpl::true_) const + { + std::locale loc = this->getloc(); + std::codecvt< char_type, char, std::mbstate_t > const& fac = std::use_facet< std::codecvt< char_type, char, std::mbstate_t > >(loc); + std::mbstate_t mbs = std::mbstate_t(); + return static_cast< size_type >(fac.length(mbs, s, s + max_size, ~static_cast< std::size_t >(0u))); + } + + //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size + static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, mpl::false_) + { + // Note: Although it's not required to be true for wchar_t, here we assume that the string has Unicode encoding. + // Compilers use some version of Unicode for wchar_t on all tested platforms, and std::locale doesn't offer a way + // to find the character boundary for character types other than char anyway. + typedef boost::locale::utf::utf_traits< CharT > utf_traits; + + size_type pos = max_size; + while (pos > 0u) { - m_Storage->append(s, static_cast< string_size_type >(n)); - return n; - } - else - { - m_Storage->append(s, max_storage_left); - return static_cast< std::streamsize >(max_storage_left); + --pos; + if (utf_traits::is_lead(s[pos])) + { + const char_type* p = s + pos; + boost::locale::utf::code_point cp = utf_traits::decode(p, s + n); + if (boost::locale::utf::is_valid_codepoint(cp) && p <= (s + max_size)) + return static_cast< size_type >(p - s); + } } + + return 0u; } //! Copy constructor (closed) diff --git a/include/boost/log/detail/code_conversion.hpp b/include/boost/log/detail/code_conversion.hpp index 86cdfd8..b39970d 100644 --- a/include/boost/log/detail/code_conversion.hpp +++ b/include/boost/log/detail/code_conversion.hpp @@ -38,64 +38,99 @@ namespace aux { // type. These conversions are used in sinks, e.g. to convert multibyte strings to wide-character filesystem paths. //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const wchar_t* str1, std::size_t len, std::string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const wchar_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc = std::locale()); //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::wstring& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc = std::locale()); #if !defined(BOOST_LOG_NO_CXX11_CODECVT_FACETS) #if !defined(BOOST_NO_CXX11_CHAR16_T) //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std::string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc = std::locale()); //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::u16string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::u16string& str2, std::size_t max_size, std::locale const& loc = std::locale()); //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std::wstring& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc = std::locale()); #endif #if !defined(BOOST_NO_CXX11_CHAR32_T) //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std::string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc = std::locale()); //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::u32string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::u32string& str2, std::size_t max_size, std::locale const& loc = std::locale()); //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std::wstring& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc = std::locale()); #endif #if !defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CXX11_CHAR32_T) //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std::u32string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::u32string& str2, std::size_t max_size, std::locale const& loc = std::locale()); //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std::u16string& str2, std::locale const& loc = std::locale()); +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::u16string& str2, std::size_t max_size, std::locale const& loc = std::locale()); #endif #endif // !defined(BOOST_LOG_NO_CXX11_CODECVT_FACETS) -//! The function converts one string to the character type of another -template< typename SourceCharT, typename SourceTraitsT, typename SourceAllocatorT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > -inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) == sizeof(TargetCharT) >::type -code_convert(std::basic_string< SourceCharT, SourceTraitsT, SourceAllocatorT > const& str1, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::locale const& = std::locale()) -{ - str2.append(reinterpret_cast< const TargetCharT* >(str1.c_str()), str1.size()); -} //! The function converts one string to the character type of another template< typename SourceCharT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > -inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) == sizeof(TargetCharT) >::type -code_convert(const SourceCharT* str1, std::size_t len, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::locale const& = std::locale()) +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) == sizeof(TargetCharT), bool >::type +code_convert(const SourceCharT* str1, std::size_t len, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::size_t max_size, std::locale const& = std::locale()) { - str2.append(reinterpret_cast< const TargetCharT* >(str1), len); + std::size_t size_left = str2.size() < max_size ? max_size - str2.size() : static_cast< std::size_t >(0u); + const bool overflow = len > size_left; + str2.append(reinterpret_cast< const TargetCharT* >(str1), overflow ? size_left : len); + return !overflow; } //! The function converts one string to the character type of another template< typename SourceCharT, typename SourceTraitsT, typename SourceAllocatorT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > -inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) != sizeof(TargetCharT) >::type +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) == sizeof(TargetCharT), bool >::type +code_convert(std::basic_string< SourceCharT, SourceTraitsT, SourceAllocatorT > const& str1, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::size_t max_size, std::locale const& loc = std::locale()) +{ + return aux::code_convert(str1.data(), str1.size(), str2, max_size, loc); +} + +//! The function converts one string to the character type of another +template< typename SourceCharT, typename SourceTraitsT, typename SourceAllocatorT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) == sizeof(TargetCharT), bool >::type code_convert(std::basic_string< SourceCharT, SourceTraitsT, SourceAllocatorT > const& str1, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::locale const& loc = std::locale()) { - aux::code_convert_impl(str1.c_str(), str1.size(), str2, loc); + return aux::code_convert(str1.data(), str1.size(), str2, str2.max_size(), loc); +} +//! The function converts one string to the character type of another +template< typename SourceCharT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) == sizeof(TargetCharT), bool >::type +code_convert(const SourceCharT* str1, std::size_t len, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::locale const& loc = std::locale()) +{ + return aux::code_convert(str1, len, str2, str2.max_size(), loc); +} + +//! The function converts one string to the character type of another +template< typename SourceCharT, typename SourceTraitsT, typename SourceAllocatorT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) != sizeof(TargetCharT), bool >::type +code_convert(std::basic_string< SourceCharT, SourceTraitsT, SourceAllocatorT > const& str1, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::locale const& loc = std::locale()) +{ + return aux::code_convert_impl(str1.c_str(), str1.size(), str2, str2.max_size(), loc); } //! The function converts one string to the character type of another template< typename SourceCharT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > -inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) != sizeof(TargetCharT) >::type +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) != sizeof(TargetCharT), bool >::type code_convert(const SourceCharT* str1, std::size_t len, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::locale const& loc = std::locale()) { - aux::code_convert_impl(str1, len, str2, loc); + return aux::code_convert_impl(str1, len, str2, str2.max_size(), loc); +} + +//! The function converts one string to the character type of another +template< typename SourceCharT, typename SourceTraitsT, typename SourceAllocatorT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) != sizeof(TargetCharT), bool >::type +code_convert(std::basic_string< SourceCharT, SourceTraitsT, SourceAllocatorT > const& str1, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::size_t max_size, std::locale const& loc = std::locale()) +{ + return aux::code_convert_impl(str1.c_str(), str1.size(), str2, max_size, loc); +} + +//! The function converts one string to the character type of another +template< typename SourceCharT, typename TargetCharT, typename TargetTraitsT, typename TargetAllocatorT > +inline typename boost::enable_if_c< is_character_type< SourceCharT >::value && is_character_type< TargetCharT >::value && sizeof(SourceCharT) != sizeof(TargetCharT), bool >::type +code_convert(const SourceCharT* str1, std::size_t len, std::basic_string< TargetCharT, TargetTraitsT, TargetAllocatorT >& str2, std::size_t max_size, std::locale const& loc = std::locale()) +{ + return aux::code_convert_impl(str1, len, str2, max_size, loc); } //! The function converts the passed string to the narrow-character encoding @@ -114,8 +149,8 @@ inline std::string const& to_narrow(std::string const& str, std::locale const&) inline std::string to_narrow(std::wstring const& str, std::locale const& loc = std::locale()) { std::string res; - aux::code_convert_impl(str.c_str(), str.size(), res, loc); -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + aux::code_convert_impl(str.c_str(), str.size(), res, res.max_size(), loc); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_HAS_NRVO) return static_cast< std::string&& >(res); #else return res; @@ -138,8 +173,8 @@ inline std::wstring const& to_wide(std::wstring const& str, std::locale const&) inline std::wstring to_wide(std::string const& str, std::locale const& loc = std::locale()) { std::wstring res; - aux::code_convert_impl(str.c_str(), str.size(), res, loc); -#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + aux::code_convert_impl(str.c_str(), str.size(), res, res.max_size(), loc); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_HAS_NRVO) return static_cast< std::wstring&& >(res); #else return res; diff --git a/include/boost/log/detail/decomposed_time.hpp b/include/boost/log/detail/decomposed_time.hpp index 6008d6a..0328106 100644 --- a/include/boost/log/detail/decomposed_time.hpp +++ b/include/boost/log/detail/decomposed_time.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -108,7 +109,7 @@ struct decomposed_time_wrapper : }; template< typename CharT > -BOOST_LOG_API void put_integer(std::basic_string< CharT >& str, uint32_t value, unsigned int width, CharT fill_char); +BOOST_LOG_API void put_integer(boost::log::aux::basic_ostringstreambuf< CharT >& strbuf, uint32_t value, unsigned int width, CharT fill_char); template< typename T, typename CharT > class date_time_formatter @@ -130,14 +131,12 @@ public: { date_time_formatter const& self; stream_type& strm; - string_type& str; value_type const& value; unsigned int literal_index, literal_pos; context(date_time_formatter const& self_, stream_type& strm_, value_type const& value_) : self(self_), strm(strm_), - str(*strm_.rdbuf()->storage()), value(value_), literal_index(0), literal_pos(0) @@ -166,12 +165,12 @@ public: m_literal_chars(that.m_literal_chars) { } - date_time_formatter(BOOST_RV_REF(date_time_formatter) that) + date_time_formatter(BOOST_RV_REF(date_time_formatter) that) BOOST_NOEXCEPT { this->swap(static_cast< date_time_formatter& >(that)); } - date_time_formatter& operator= (date_time_formatter that) + date_time_formatter& operator= (date_time_formatter that) BOOST_NOEXCEPT { this->swap(that); return *this; @@ -200,7 +199,7 @@ public: m_formatters.push_back(&date_time_formatter_::format_literal); } - void swap(date_time_formatter& that) + void swap(date_time_formatter& that) BOOST_NOEXCEPT { m_formatters.swap(that.m_formatters); m_literal_lens.swap(that.m_literal_lens); @@ -220,55 +219,55 @@ public: static void format_full_year(context& ctx) { - (put_integer)(ctx.str, ctx.value.year, 4, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.year, 4, static_cast< char_type >('0')); } static void format_short_year(context& ctx) { - (put_integer)(ctx.str, ctx.value.year % 100u, 2, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.year % 100u, 2, static_cast< char_type >('0')); } static void format_numeric_month(context& ctx) { - (put_integer)(ctx.str, ctx.value.month, 2, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.month, 2, static_cast< char_type >('0')); } template< char_type FillCharV > static void format_month_day(context& ctx) { - (put_integer)(ctx.str, ctx.value.day, 2, static_cast< char_type >(FillCharV)); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.day, 2, static_cast< char_type >(FillCharV)); } static void format_week_day(context& ctx) { - (put_integer)(ctx.str, static_cast< decomposed_time const& >(ctx.value).week_day(), 1, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), static_cast< decomposed_time const& >(ctx.value).week_day(), 1, static_cast< char_type >('0')); } template< char_type FillCharV > static void format_hours(context& ctx) { - (put_integer)(ctx.str, ctx.value.hours, 2, static_cast< char_type >(FillCharV)); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.hours, 2, static_cast< char_type >(FillCharV)); } template< char_type FillCharV > static void format_hours_12(context& ctx) { - (put_integer)(ctx.str, ctx.value.hours % 12u + 1u, 2, static_cast< char_type >(FillCharV)); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.hours % 12u + 1u, 2, static_cast< char_type >(FillCharV)); } static void format_minutes(context& ctx) { - (put_integer)(ctx.str, ctx.value.minutes, 2, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.minutes, 2, static_cast< char_type >('0')); } static void format_seconds(context& ctx) { - (put_integer)(ctx.str, ctx.value.seconds, 2, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.seconds, 2, static_cast< char_type >('0')); } static void format_fractional_seconds(context& ctx) { - (put_integer)(ctx.str, ctx.value.subseconds, decomposed_time::subseconds_digits10, static_cast< char_type >('0')); + (put_integer)(*ctx.strm.rdbuf(), ctx.value.subseconds, decomposed_time::subseconds_digits10, static_cast< char_type >('0')); } template< bool UpperCaseV > @@ -277,16 +276,16 @@ public: static const char_type am[] = { static_cast< char_type >(UpperCaseV ? 'A' : 'a'), static_cast< char_type >(UpperCaseV ? 'M' : 'm'), static_cast< char_type >(0) }; static const char_type pm[] = { static_cast< char_type >(UpperCaseV ? 'P' : 'p'), static_cast< char_type >(UpperCaseV ? 'M' : 'm'), static_cast< char_type >(0) }; - ctx.str.append(((static_cast< decomposed_time const& >(ctx.value).hours > 11) ? pm : am), 2u); + ctx.strm.rdbuf()->append(((static_cast< decomposed_time const& >(ctx.value).hours > 11) ? pm : am), 2u); } template< bool DisplayPositiveV > static void format_sign(context& ctx) { if (static_cast< decomposed_time const& >(ctx.value).negative) - ctx.str.push_back('-'); + ctx.strm.rdbuf()->push_back('-'); else if (DisplayPositiveV) - ctx.str.push_back('+'); + ctx.strm.rdbuf()->push_back('+'); } private: @@ -296,7 +295,7 @@ private: ++ctx.literal_index; ctx.literal_pos += len; const char_type* lit = ctx.self.m_literal_chars.c_str(); - ctx.str.append(lit + pos, len); + ctx.strm.rdbuf()->append(lit + pos, len); } }; diff --git a/include/boost/log/detail/format.hpp b/include/boost/log/detail/format.hpp index 7f65df1..449f85e 100644 --- a/include/boost/log/detail/format.hpp +++ b/include/boost/log/detail/format.hpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -188,31 +188,13 @@ public: } //! Creates a pump that will receive all format arguments and put the formatted string into the stream - pump make_pump(stream_type& strm) BOOST_NOEXCEPT + pump make_pump(stream_type& strm) { + // Flush the stream beforehand so that the pump can safely switch the stream storage string + strm.flush(); return pump(*this, strm); } - //! Composes the final string from the formatted pieces - void compose(string_type& str) const - { - typename format_description_type::format_element_list::const_iterator it = m_format.format_elements.begin(), end = m_format.format_elements.end(); - for (; it != end; ++it) - { - if (it->arg_number >= 0) - { - // This is a placeholder - str.append(m_formatting_params[it->arg_number].target); - } - else - { - // This is a literal - const char_type* p = m_format.literal_chars.c_str() + it->literal_start_pos; - str.append(p, it->literal_len); - } - } - } - //! Composes the final string from the formatted pieces string_type str() const { @@ -236,6 +218,28 @@ private: } } } + + //! Composes the final string from the formatted pieces + template< typename T > + void compose(T& str) const + { + typename format_description_type::format_element_list::const_iterator it = m_format.format_elements.begin(), end = m_format.format_elements.end(); + for (; it != end; ++it) + { + if (it->arg_number >= 0) + { + // This is a placeholder + string_type const& target = m_formatting_params[it->arg_number].target; + str.append(target.data(), target.size()); + } + else + { + // This is a literal + const char_type* p = m_format.literal_chars.c_str() + it->literal_start_pos; + str.append(p, it->literal_len); + } + } + } }; //! The pump receives arguments and formats them into strings. At destruction the pump composes the final string in the attached stream. @@ -248,18 +252,18 @@ private: //! The guard temporarily replaces storage string in the specified stream struct scoped_storage { - scoped_storage(stream_type& strm, string_type& storage) : m_stream(strm), m_storage_backup(*strm.rdbuf()->storage()) + scoped_storage(stream_type& strm, string_type& storage) : m_stream(strm), m_storage_state_backup(strm.rdbuf()->get_storage_state()) { strm.attach(storage); } ~scoped_storage() { - m_stream.attach(m_storage_backup); + m_stream.rdbuf()->set_storage_state(m_storage_state_backup); } private: stream_type& m_stream; - string_type& m_storage_backup; + typename stream_type::streambuf_type::storage_state m_storage_state_backup; }; private: @@ -296,7 +300,7 @@ public: { // Compose the final string in the stream buffer m_stream->flush(); - m_owner->compose(*m_stream->rdbuf()->storage()); + m_owner->compose(*m_stream->rdbuf()); } } } diff --git a/include/boost/log/expressions/formatters/char_decorator.hpp b/include/boost/log/expressions/formatters/char_decorator.hpp index d053673..9c29622 100644 --- a/include/boost/log/expressions/formatters/char_decorator.hpp +++ b/include/boost/log/expressions/formatters/char_decorator.hpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -300,12 +300,12 @@ public: typename string_type::size_type const start_pos = strm.rdbuf()->storage()->size(); // Invoke the adopted formatter - typedef typename result< this_type(ContextT const&) >::type result_type; phoenix::eval(m_subactor, ctx); // Flush the buffered characters and apply decorations strm.flush(); m_impl(*strm.rdbuf()->storage(), start_pos); + strm.rdbuf()->ensure_max_size(); return strm; } @@ -323,12 +323,12 @@ public: typename string_type::size_type const start_pos = strm.rdbuf()->storage()->size(); // Invoke the adopted formatter - typedef typename result< const this_type(ContextT const&) >::type result_type; phoenix::eval(m_subactor, ctx); // Flush the buffered characters and apply decorations strm.flush(); m_impl(*strm.rdbuf()->storage(), start_pos); + strm.rdbuf()->ensure_max_size(); return strm; } diff --git a/include/boost/log/expressions/formatters/max_size_decorator.hpp b/include/boost/log/expressions/formatters/max_size_decorator.hpp new file mode 100644 index 0000000..fe70bef --- /dev/null +++ b/include/boost/log/expressions/formatters/max_size_decorator.hpp @@ -0,0 +1,531 @@ +/* + * Copyright Andrey Semashev 2016. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file formatters/max_size_decorator.hpp + * \author Andrey Semashev + * \date 06.07.2016 + * + * The header contains implementation of a string length limiting decorator. + */ + +#ifndef BOOST_LOG_EXPRESSIONS_FORMATTERS_MAX_SIZE_DECORATOR_HPP_INCLUDED_ +#define BOOST_LOG_EXPRESSIONS_FORMATTERS_MAX_SIZE_DECORATOR_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace expressions { + +namespace aux { + +//! String size limiting decorator stream output terminal +template< typename LeftT, typename SubactorT, typename CharT > +class max_size_decorator_output_terminal +{ +private: + //! Self type + typedef max_size_decorator_output_terminal< LeftT, SubactorT, CharT > this_type; + +public: + //! Internal typedef for type categorization + typedef void _is_boost_log_terminal; + + //! Character type + typedef CharT char_type; + //! String type + typedef std::basic_string< char_type > string_type; + //! String size type + typedef std::size_t size_type; + //! Adopted actor type + typedef SubactorT subactor_type; + + //! Result type definition + template< typename > + struct result; + + template< typename ThisT, typename ContextT > + struct result< ThisT(ContextT) > + { + typedef typename remove_cv< typename remove_reference< ContextT >::type >::type context_type; + typedef typename phoenix::evaluator::impl< + typename LeftT::proto_base_expr&, + context_type, + phoenix::unused + >::result_type type; + }; + +private: + //! Left argument actor + LeftT m_left; + //! Adopted formatter actor + subactor_type m_subactor; + //! Max size of the formatted string produced by the adopted formatter + size_type m_max_size; + //! Overflow marker + string_type m_overflow_marker; + +public: + /*! + * Initializing constructor. Creates decorator of the \a fmt formatter with the specified \a decorations. + */ + max_size_decorator_output_terminal(LeftT const& left, subactor_type const& sub, size_type max_size, string_type const& overflow_marker = string_type()) : + m_left(left), m_subactor(sub), m_max_size(max_size), m_overflow_marker(overflow_marker) + { + } + /*! + * Copy constructor + */ + max_size_decorator_output_terminal(max_size_decorator_output_terminal const& that) : + m_left(that.m_left), m_subactor(that.m_subactor), m_max_size(that.m_max_size), m_overflow_marker(that.m_overflow_marker) + { + } + + /*! + * Invokation operator + */ + template< typename ContextT > + typename result< this_type(ContextT const&) >::type operator() (ContextT const& ctx) + { + // Flush the stream and keep the current write position in the target string + typedef typename result< this_type(ContextT const&) >::type result_type; + result_type strm = phoenix::eval(m_left, ctx); + strm.flush(); + + if (!strm.rdbuf()->storage_overflow()) + { + const size_type old_max_size = strm.rdbuf()->max_size(); + const size_type start_pos = strm.rdbuf()->storage()->size(), max_size_left = strm.rdbuf()->storage()->max_size() - start_pos; + strm.rdbuf()->max_size(start_pos + (m_max_size <= max_size_left ? m_max_size : max_size_left)); + + try + { + // Invoke the adopted formatter + phoenix::eval(m_subactor, ctx); + + // Flush the buffered characters and apply decorations + strm.flush(); + + if (strm.rdbuf()->storage_overflow()) + { + // Append overflow marker + strm.rdbuf()->max_size(strm.rdbuf()->storage()->max_size()); + strm.rdbuf()->storage_overflow(false); + strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size()); + } + + // Restore the original size limit + strm.rdbuf()->max_size(old_max_size); + } + catch (...) + { + strm.rdbuf()->storage_overflow(false); + strm.rdbuf()->max_size(old_max_size); + throw; + } + } + + return strm; + } + + /*! + * Invokation operator + */ + template< typename ContextT > + typename result< const this_type(ContextT const&) >::type operator() (ContextT const& ctx) const + { + // Flush the stream and keep the current write position in the target string + typedef typename result< const this_type(ContextT const&) >::type result_type; + result_type strm = phoenix::eval(m_left, ctx); + strm.flush(); + + if (!strm.rdbuf()->storage_overflow()) + { + const size_type old_max_size = strm.rdbuf()->max_size(); + const size_type start_pos = strm.rdbuf()->storage()->size(), max_size_left = strm.rdbuf()->storage()->max_size() - start_pos; + strm.rdbuf()->max_size(start_pos + (m_max_size <= max_size_left ? m_max_size : max_size_left)); + + try + { + // Invoke the adopted formatter + phoenix::eval(m_subactor, ctx); + + // Flush the buffered characters and apply decorations + strm.flush(); + + if (strm.rdbuf()->storage_overflow()) + { + // Append overflow marker + strm.rdbuf()->max_size(strm.rdbuf()->storage()->max_size()); + strm.rdbuf()->storage_overflow(false); + strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size()); + } + + // Restore the original size limit + strm.rdbuf()->max_size(old_max_size); + } + catch (...) + { + strm.rdbuf()->storage_overflow(false); + strm.rdbuf()->max_size(old_max_size); + throw; + } + } + + return strm; + } + + BOOST_DELETED_FUNCTION(max_size_decorator_output_terminal()) +}; + +} // namespace aux + +/*! + * String size limiting decorator terminal class. This formatter allows to limit the maximum total length + * of the strings generated by other formatters. + * + * The \c max_size_decorator_terminal class aggregates the formatter being decorated, the maximum string length + * it can produce and an optional truncation marker string, which will get appended if the limit is exceeded. Note that + * the marker length is not accounted for by the limit. + * The \c max_size_decorator_terminal class is a formatter itself, so it can be used to construct + * more complex formatters, including nesting decorators. + */ +template< typename SubactorT, typename CharT > +class max_size_decorator_terminal +{ +private: + //! Self type + typedef max_size_decorator_terminal< SubactorT, CharT > this_type; + +public: + //! Internal typedef for type categorization + typedef void _is_boost_log_terminal; + + //! Character type + typedef CharT char_type; + //! String type + typedef std::basic_string< char_type > string_type; + //! String size type + typedef std::size_t size_type; + //! Stream type + typedef basic_formatting_ostream< char_type > stream_type; + //! Adopted actor type + typedef SubactorT subactor_type; + + //! Result type definition + typedef string_type result_type; + +private: + //! Adopted formatter actor + subactor_type m_subactor; + //! Max size of the formatted string produced by the adopted formatter + size_type m_max_size; + //! Overflow marker + string_type m_overflow_marker; + +public: + /*! + * Initializing constructor. + */ + max_size_decorator_terminal(subactor_type const& sub, size_type max_size, string_type const& overflow_marker = string_type()) : + m_subactor(sub), m_max_size(max_size), m_overflow_marker(overflow_marker) + { + } + /*! + * Copy constructor + */ + max_size_decorator_terminal(max_size_decorator_terminal const& that) : + m_subactor(that.m_subactor), m_max_size(that.m_max_size), m_overflow_marker(that.m_overflow_marker) + { + } + + /*! + * \returns Adopted subactor + */ + subactor_type const& get_subactor() const + { + return m_subactor; + } + + /*! + * \returns Max string size limit + */ + size_type get_max_size() const + { + return m_max_size; + } + + /*! + * \returns Max string size limit + */ + string_type const& get_overflow_marker() const + { + return m_overflow_marker; + } + + /*! + * Invokation operator + */ + template< typename ContextT > + result_type operator() (ContextT const& ctx) + { + string_type str; + stream_type strm(str); + strm.rdbuf()->max_size(m_max_size); + + // Invoke the adopted formatter + typedef phoenix::vector3< + subactor_type*, + typename fusion::result_of::at_c< + typename remove_cv< + typename remove_reference< + typename phoenix::result_of::env< ContextT const& >::type + >::type + >::type::args_type, + 0 + >::type, + stream_type& + > env_type; + env_type env = { boost::addressof(m_subactor), fusion::at_c< 0 >(phoenix::env(ctx).args()), strm }; + phoenix::eval(m_subactor, phoenix::make_context(env, phoenix::actions(ctx))); + + // Flush the buffered characters and see of overflow happened + strm.flush(); + + if (strm.rdbuf()->storage_overflow()) + { + strm.rdbuf()->max_size(str.max_size()); + strm.rdbuf()->storage_overflow(false); + strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size()); + strm.flush(); + } + + return BOOST_LOG_NRVO_RESULT(str); + } + + /*! + * Invokation operator + */ + template< typename ContextT > + result_type operator() (ContextT const& ctx) const + { + string_type str; + stream_type strm(str); + strm.rdbuf()->max_size(m_max_size); + + // Invoke the adopted formatter + typedef phoenix::vector3< + const subactor_type*, + typename fusion::result_of::at_c< + typename remove_cv< + typename remove_reference< + typename phoenix::result_of::env< ContextT const& >::type + >::type + >::type::args_type, + 0 + >::type, + stream_type& + > env_type; + env_type env = { boost::addressof(m_subactor), fusion::at_c< 0 >(phoenix::env(ctx).args()), strm }; + phoenix::eval(m_subactor, phoenix::make_context(env, phoenix::actions(ctx))); + + // Flush the buffered characters and see of overflow happened + strm.flush(); + + if (strm.rdbuf()->storage_overflow()) + { + strm.rdbuf()->max_size(str.max_size()); + strm.rdbuf()->storage_overflow(false); + strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size()); + strm.flush(); + } + + return BOOST_LOG_NRVO_RESULT(str); + } + + BOOST_DELETED_FUNCTION(max_size_decorator_terminal()) +}; + +/*! + * Character decorator actor + */ +template< typename SubactorT, typename CharT, template< typename > class ActorT = phoenix::actor > +class max_size_decorator_actor : + public ActorT< max_size_decorator_terminal< SubactorT, CharT > > +{ +public: + //! Base terminal type + typedef max_size_decorator_terminal< SubactorT, CharT > terminal_type; + //! Character type + typedef typename terminal_type::char_type char_type; + + //! Base actor type + typedef ActorT< terminal_type > base_type; + +public: + //! Initializing constructor + explicit max_size_decorator_actor(base_type const& act) : base_type(act) + { + } + + //! Returns reference to the terminal + terminal_type const& get_terminal() const + { + return this->proto_expr_.child0; + } +}; + +#ifndef BOOST_LOG_DOXYGEN_PASS + +#define BOOST_LOG_AUX_OVERLOAD(left_ref, right_ref)\ + template< typename LeftExprT, typename SubactorT, typename CharT, template< typename > class ActorT >\ + BOOST_FORCEINLINE phoenix::actor< aux::max_size_decorator_output_terminal< phoenix::actor< LeftExprT >, SubactorT, CharT > >\ + operator<< (phoenix::actor< LeftExprT > left_ref left, max_size_decorator_actor< SubactorT, CharT, ActorT > right_ref right)\ + {\ + typedef aux::max_size_decorator_output_terminal< phoenix::actor< LeftExprT >, SubactorT, CharT > terminal_type;\ + phoenix::actor< terminal_type > actor = {{ terminal_type(left, right.get_terminal().get_subactor(), right.get_terminal().get_max_size(), right.get_terminal().get_overflow_marker()) }};\ + return actor;\ + } + +#include + +#undef BOOST_LOG_AUX_OVERLOAD + +#endif // BOOST_LOG_DOXYGEN_PASS + +namespace aux { + +template< typename CharT > +class max_size_decorator_gen +{ +private: + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::size_t size_type; + +private: + size_type m_max_size; + string_type m_overflow_marker; + +public: + explicit max_size_decorator_gen(size_type max_size, string_type const& overflow_marker = string_type()) : + m_max_size(max_size), m_overflow_marker(overflow_marker) + { + } + + template< typename SubactorT > + BOOST_FORCEINLINE max_size_decorator_actor< SubactorT, char_type > operator[] (SubactorT const& subactor) const + { + typedef max_size_decorator_actor< SubactorT, char_type > result_type; + typedef typename result_type::terminal_type terminal_type; + typename result_type::base_type act = {{ terminal_type(subactor, m_max_size, m_overflow_marker) }}; + return result_type(act); + } +}; + +} // namespace aux + +/*! + * The function returns a decorator generator object. The generator provides operator[] that can be used + * to construct the actual decorator. + * + * \param max_size The maximum number of characters (i.e. string element objects) that the decorated formatter can produce. + */ +template< typename CharT > +BOOST_FORCEINLINE aux::max_size_decorator_gen< CharT > max_size_decor(std::size_t max_size) +{ + return aux::max_size_decorator_gen< CharT >(max_size); +} + +/*! + * The function returns a decorator generator object. The generator provides operator[] that can be used + * to construct the actual decorator. + * + * \param max_size The maximum number of characters (i.e. string element objects) that the decorated formatter can produce. + * \param overflow_marker The marker string which is appended to the output if the \a max_size limit is exceeded. Must be + * a non-null pointer to a zero-terminated string. + * + * \note The \a overflow_marker length is not included in the \a max_size limit. + */ +template< typename CharT > +BOOST_FORCEINLINE aux::max_size_decorator_gen< CharT > max_size_decor(std::size_t max_size, const CharT* overflow_marker) +{ + return aux::max_size_decorator_gen< CharT >(max_size, overflow_marker); +} + +/*! + * The function returns a decorator generator object. The generator provides operator[] that can be used + * to construct the actual decorator. + * + * \param max_size The maximum number of characters (i.e. string element objects) that the decorated formatter can produce. + * \param overflow_marker The marker string which is appended to the output if the \a max_size limit is exceeded. + * + * \note The \a overflow_marker length is not included in the \a max_size limit. + */ +template< typename CharT > +BOOST_FORCEINLINE aux::max_size_decorator_gen< CharT > max_size_decor(std::size_t max_size, std::basic_string< CharT > const& overflow_marker) +{ + return aux::max_size_decorator_gen< CharT >(max_size, overflow_marker); +} + +} // namespace expressions + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +#ifndef BOOST_LOG_DOXYGEN_PASS + +namespace phoenix { + +namespace result_of { + +template< typename SubactorT, typename CharT > +struct is_nullary< custom_terminal< boost::log::expressions::max_size_decorator_terminal< SubactorT, CharT > > > : + public mpl::false_ +{ +}; + +template< typename LeftT, typename SubactorT, typename CharT > +struct is_nullary< custom_terminal< boost::log::expressions::aux::max_size_decorator_output_terminal< LeftT, SubactorT, CharT > > > : + public mpl::false_ +{ +}; + +} // namespace result_of + +} // namespace phoenix + +#endif + +} // namespace boost + +#include + +#endif // BOOST_LOG_EXPRESSIONS_FORMATTERS_MAX_SIZE_DECORATOR_HPP_INCLUDED_ diff --git a/include/boost/log/sources/basic_logger.hpp b/include/boost/log/sources/basic_logger.hpp index bc35acf..3403928 100644 --- a/include/boost/log/sources/basic_logger.hpp +++ b/include/boost/log/sources/basic_logger.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/log/sources/record_ostream.hpp b/include/boost/log/sources/record_ostream.hpp index c1e8059..b8fcd9f 100644 --- a/include/boost/log/sources/record_ostream.hpp +++ b/include/boost/log/sources/record_ostream.hpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/log/utility/formatting_ostream.hpp b/include/boost/log/utility/formatting_ostream.hpp index a2d78bc..4a79998 100644 --- a/include/boost/log/utility/formatting_ostream.hpp +++ b/include/boost/log/utility/formatting_ostream.hpp @@ -245,7 +245,7 @@ public: */ string_type const& str() const { - string_type* storage = m_streambuf.storage(); + string_type* const storage = m_streambuf.storage(); BOOST_ASSERT(storage != NULL); m_streambuf.pubsync(); @@ -372,8 +372,12 @@ public: { m_stream.flush(); - string_type* storage = m_streambuf.storage(); - aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc()); + if (!m_streambuf.storage_overflow()) + { + string_type* storage = m_streambuf.storage(); + if (!aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_streambuf.max_size(), m_stream.getloc())) + m_streambuf.storage_overflow(true); + } } return *this; @@ -668,7 +672,7 @@ private: m_stream.flush(); if (m_stream.width() <= size) - m_streambuf.storage()->append(p, static_cast< std::size_t >(size)); + m_streambuf.append(p, static_cast< std::size_t >(size)); else this->aligned_write(p, size); @@ -687,7 +691,13 @@ private: m_stream.flush(); if (m_stream.width() <= size) - aux::code_convert(p, static_cast< std::size_t >(size), *m_streambuf.storage(), m_stream.getloc()); + { + if (!m_streambuf.storage_overflow()) + { + if (!aux::code_convert(p, static_cast< std::size_t >(size), *m_streambuf.storage(), m_streambuf.max_size(), m_stream.getloc())) + m_streambuf.storage_overflow(true); + } + } else this->aligned_write(p, size); @@ -782,19 +792,18 @@ BOOST_CONSTEXPR_OR_CONST typename basic_formatting_ostream< CharT, TraitsT, Allo template< typename CharT, typename TraitsT, typename AllocatorT > void basic_formatting_ostream< CharT, TraitsT, AllocatorT >::aligned_write(const char_type* p, std::streamsize size) { - string_type* const storage = m_streambuf.storage(); typename string_type::size_type const alignment_size = static_cast< typename string_type::size_type >(m_stream.width() - size); const bool align_left = (m_stream.flags() & ostream_type::adjustfield) == ostream_type::left; if (align_left) { - storage->append(p, static_cast< std::size_t >(size)); - storage->append(alignment_size, m_stream.fill()); + m_streambuf.append(p, static_cast< std::size_t >(size)); + m_streambuf.append(alignment_size, m_stream.fill()); } else { - storage->append(alignment_size, m_stream.fill()); - storage->append(p, static_cast< std::size_t >(size)); + m_streambuf.append(alignment_size, m_stream.fill()); + m_streambuf.append(p, static_cast< std::size_t >(size)); } } @@ -808,13 +817,21 @@ void basic_formatting_ostream< CharT, TraitsT, AllocatorT >::aligned_write(const const bool align_left = (m_stream.flags() & ostream_type::adjustfield) == ostream_type::left; if (align_left) { - aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc()); - storage->append(alignment_size, m_stream.fill()); + if (!m_streambuf.storage_overflow()) + { + if (!aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_streambuf.max_size(), m_stream.getloc())) + m_streambuf.storage_overflow(true); + } + m_streambuf.append(alignment_size, m_stream.fill()); } else { - storage->append(alignment_size, m_stream.fill()); - aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc()); + m_streambuf.append(alignment_size, m_stream.fill()); + if (!m_streambuf.storage_overflow()) + { + if (!aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_streambuf.max_size(), m_stream.getloc())) + m_streambuf.storage_overflow(true); + } } } diff --git a/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp b/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp index c580213..dcf2cd0 100644 --- a/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp +++ b/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/log/utility/value_ref.hpp b/include/boost/log/utility/value_ref.hpp index 60b8f18..1f9a360 100644 --- a/include/boost/log/utility/value_ref.hpp +++ b/include/boost/log/utility/value_ref.hpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/code_conversion.cpp b/src/code_conversion.cpp index cf07d05..1504b2a 100644 --- a/src/code_conversion.cpp +++ b/src/code_conversion.cpp @@ -37,42 +37,44 @@ namespace aux { BOOST_LOG_ANONYMOUS_NAMESPACE { - //! The function performs character conversion with the specified facet - template< typename LocalCharT > - inline std::codecvt_base::result convert( - std::codecvt< LocalCharT, char, std::mbstate_t > const& fac, - std::mbstate_t& state, - const char*& pSrcBegin, - const char* pSrcEnd, - LocalCharT*& pDstBegin, - LocalCharT* pDstEnd) - { - return fac.in(state, pSrcBegin, pSrcEnd, pSrcBegin, pDstBegin, pDstEnd, pDstBegin); - } +//! The function performs character conversion with the specified facet +template< typename LocalCharT > +inline std::codecvt_base::result convert( + std::codecvt< LocalCharT, char, std::mbstate_t > const& fac, + std::mbstate_t& state, + const char*& pSrcBegin, + const char* pSrcEnd, + LocalCharT*& pDstBegin, + LocalCharT* pDstEnd) +{ + return fac.in(state, pSrcBegin, pSrcEnd, pSrcBegin, pDstBegin, pDstEnd, pDstBegin); +} - //! The function performs character conversion with the specified facet - template< typename LocalCharT > - inline std::codecvt_base::result convert( - std::codecvt< LocalCharT, char, std::mbstate_t > const& fac, - std::mbstate_t& state, - const LocalCharT*& pSrcBegin, - const LocalCharT* pSrcEnd, - char*& pDstBegin, - char* pDstEnd) - { - return fac.out(state, pSrcBegin, pSrcEnd, pSrcBegin, pDstBegin, pDstEnd, pDstBegin); - } +//! The function performs character conversion with the specified facet +template< typename LocalCharT > +inline std::codecvt_base::result convert( + std::codecvt< LocalCharT, char, std::mbstate_t > const& fac, + std::mbstate_t& state, + const LocalCharT*& pSrcBegin, + const LocalCharT* pSrcEnd, + char*& pDstBegin, + char* pDstEnd) +{ + return fac.out(state, pSrcBegin, pSrcEnd, pSrcBegin, pDstBegin, pDstEnd, pDstBegin); +} } // namespace template< typename SourceCharT, typename TargetCharT, typename FacetT > -inline void code_convert(const SourceCharT* begin, const SourceCharT* end, std::basic_string< TargetCharT >& converted, FacetT const& fac) +inline std::size_t code_convert(const SourceCharT* begin, const SourceCharT* end, std::basic_string< TargetCharT >& converted, std::size_t max_size, FacetT const& fac) { typedef typename FacetT::state_type state_type; TargetCharT converted_buffer[256]; + const SourceCharT* const original_begin = begin; state_type state = state_type(); - while (begin != end) + std::size_t buf_size = std::min(max_size, sizeof(converted_buffer) / sizeof(*converted_buffer)); + while (begin != end && buf_size > 0u) { TargetCharT* dest = converted_buffer; std::codecvt_base::result res = convert( @@ -81,7 +83,7 @@ inline void code_convert(const SourceCharT* begin, const SourceCharT* end, std:: begin, end, dest, - dest + sizeof(converted_buffer) / sizeof(*converted_buffer)); + dest + buf_size); switch (res) { @@ -89,8 +91,19 @@ inline void code_convert(const SourceCharT* begin, const SourceCharT* end, std:: // All characters were successfully converted // NOTE: MSVC 11 also returns ok when the source buffer was only partially consumed, so we also check that the begin pointer has reached the end. converted.append(converted_buffer, dest); + max_size -= dest - converted_buffer; break; + case std::codecvt_base::noconv: + { + // Not possible, unless both character types are actually equivalent + const std::size_t size = std::min(max_size, static_cast< std::size_t >(end - begin)); + converted.append(begin, begin + size); + begin += size; + max_size -= size; + } + goto done; + case std::codecvt_base::partial: // Some characters were converted, some were not if (dest != converted_buffer) @@ -98,39 +111,42 @@ inline void code_convert(const SourceCharT* begin, const SourceCharT* end, std:: // Some conversion took place, so it seems like // the destination buffer might not have been long enough converted.append(converted_buffer, dest); + max_size -= dest - converted_buffer; // ...and go on for the next part break; } else { - // Nothing was converted, looks like the tail of the - // source buffer contains only part of the last character. - // Leave it as it is. - return; - } + // Nothing was converted + if (begin == end) + goto done; - case std::codecvt_base::noconv: - // Not possible, unless both character types are actually equivalent - converted.append(reinterpret_cast< const TargetCharT* >(begin), reinterpret_cast< const TargetCharT* >(end)); - return; + // Looks like the tail of the source buffer contains only part of the last character. + // In this case we intentionally fall through to throw an exception. + } default: // std::codecvt_base::error BOOST_LOG_THROW_DESCR(conversion_error, "Could not convert character encoding"); } + + buf_size = std::min(max_size, sizeof(converted_buffer) / sizeof(*converted_buffer)); } + +done: + return static_cast< std::size_t >(begin - original_begin); } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const wchar_t* str1, std::size_t len, std::string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const wchar_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) { - code_convert(str1, str1 + len, str2, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)); + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == len; } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::wstring& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc) { - code_convert(str1, str1 + len, str2, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)); + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == len; } #if !defined(BOOST_LOG_NO_CXX11_CODECVT_FACETS) @@ -138,23 +154,24 @@ BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::wst #if !defined(BOOST_NO_CXX11_CHAR16_T) //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std::string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) { - code_convert(str1, str1 + len, str2, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)) == len; } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::u16string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::u16string& str2, std::size_t max_size, std::locale const& loc) { - code_convert(str1, str1 + len, str2, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)) == len; } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std::wstring& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc) { std::string temp_str; - code_convert(str1, str1 + len, temp_str, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); - code_convert(temp_str.c_str(), temp_str.c_str() + temp_str.size(), str2, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)); + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == temp_size; } #endif @@ -162,23 +179,24 @@ BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std: #if !defined(BOOST_NO_CXX11_CHAR32_T) //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std::string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) { - code_convert(str1, str1 + len, str2, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)) == len; } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char* str1, std::size_t len, std::u32string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::u32string& str2, std::size_t max_size, std::locale const& loc) { - code_convert(str1, str1 + len, str2, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)) == len; } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std::wstring& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc) { std::string temp_str; - code_convert(str1, str1 + len, temp_str, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); - code_convert(temp_str.c_str(), temp_str.c_str() + temp_str.size(), str2, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)); + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == temp_size; } #endif @@ -186,19 +204,21 @@ BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std: #if !defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CXX11_CHAR32_T) //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char16_t* str1, std::size_t len, std::u32string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::u32string& str2, std::size_t max_size, std::locale const& loc) { std::string temp_str; - code_convert(str1, str1 + len, temp_str, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); - code_convert(temp_str.c_str(), temp_str.c_str() + temp_str.size(), str2, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)) == temp_size; } //! The function converts one string to the character type of another -BOOST_LOG_API void code_convert_impl(const char32_t* str1, std::size_t len, std::u16string& str2, std::locale const& loc) +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::u16string& str2, std::size_t max_size, std::locale const& loc) { std::string temp_str; - code_convert(str1, str1 + len, temp_str, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); - code_convert(temp_str.c_str(), temp_str.c_str() + temp_str.size(), str2, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)) == temp_size; } #endif diff --git a/src/core.cpp b/src/core.cpp index b92c748..c55968c 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -334,9 +334,9 @@ public: template< typename SourceAttributesT > BOOST_FORCEINLINE record open_record(BOOST_FWD_REF(SourceAttributesT) source_attributes) { - record rec; + record rec; - // Try a quick win first + // Try a quick win first if (m_enabled) try { thread_data* tsd = get_thread_data(); @@ -372,7 +372,7 @@ public: if (rec_impl && rec_impl->accepting_sink_count() == 0) { // No sinks accepted the record - rec.reset(); + rec.reset(); goto done; } @@ -395,11 +395,11 @@ public: throw; m_exception_handler(); - rec.reset(); + rec.reset(); } - done: - return BOOST_LOG_NRVO_RESULT(rec); + done: + return BOOST_LOG_NRVO_RESULT(rec); } //! The method returns the current thread-specific data diff --git a/src/date_time_format_parser.cpp b/src/date_time_format_parser.cpp index 0ad8694..24ddda4 100644 --- a/src/date_time_format_parser.cpp +++ b/src/date_time_format_parser.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -374,7 +375,7 @@ BOOST_LOG_API void parse_date_time_format(const CharT* begin, const CharT* end, } template< typename CharT > -BOOST_LOG_API void put_integer(std::basic_string< CharT >& str, uint32_t value, unsigned int width, CharT fill_char) +BOOST_LOG_API void put_integer(boost::log::aux::basic_ostringstreambuf< CharT >& strbuf, uint32_t value, unsigned int width, CharT fill_char) { typedef CharT char_type; char_type buf[std::numeric_limits< uint32_t >::digits10 + 2]; @@ -384,8 +385,8 @@ BOOST_LOG_API void put_integer(std::basic_string< CharT >& str, uint32_t value, karma::generate(p, uint_gen(), value); const std::size_t len = p - buf; if (len < width) - str.insert(str.end(), width - len, fill_char); - str.append(buf, p); + strbuf.append(width - len, fill_char); + strbuf.append(buf, len); } #ifdef BOOST_LOG_USE_CHAR @@ -397,7 +398,7 @@ void parse_time_format(const char* begin, const char* end, time_format_parser_ca template BOOST_LOG_API void parse_date_time_format(const char* begin, const char* end, date_time_format_parser_callback< char >& callback); template BOOST_LOG_API -void put_integer(std::basic_string< char >& str, uint32_t value, unsigned int width, char fill_char); +void put_integer(boost::log::aux::basic_ostringstreambuf< char >& strbuf, uint32_t value, unsigned int width, char fill_char); #endif // BOOST_LOG_USE_CHAR @@ -410,7 +411,7 @@ void parse_time_format(const wchar_t* begin, const wchar_t* end, time_format_par template BOOST_LOG_API void parse_date_time_format(const wchar_t* begin, const wchar_t* end, date_time_format_parser_callback< wchar_t >& callback); template BOOST_LOG_API -void put_integer(std::basic_string< wchar_t >& str, uint32_t value, unsigned int width, wchar_t fill_char); +void put_integer(boost::log::aux::basic_ostringstreambuf< wchar_t >& strbuf, uint32_t value, unsigned int width, wchar_t fill_char); #endif // BOOST_LOG_USE_WCHAR_T diff --git a/src/named_scope_format_parser.cpp b/src/named_scope_format_parser.cpp index d48eca2..16e7bd2 100644 --- a/src/named_scope_format_parser.cpp +++ b/src/named_scope_format_parser.cpp @@ -575,15 +575,15 @@ public: result_type operator() (stream_type& strm, value_type const& value) const { strm.flush(); - typedef typename stream_type::streambuf_type streambuf_type; - string_type& str = *static_cast< streambuf_type* >(strm.rdbuf())->storage(); char_type buf[std::numeric_limits< unsigned int >::digits10 + 2]; char_type* p = buf; typedef karma::uint_generator< unsigned int, 10 > uint_gen; karma::generate(p, uint_gen(), value.line); - str.append(buf, p); + + typedef typename stream_type::streambuf_type streambuf_type; + static_cast< streambuf_type* >(strm.rdbuf())->append(buf, static_cast< std::size_t >(p - buf)); } };