| 1 | /* |
| 2 | * Copyright Andrey Semashev 2007 - 2016. |
| 3 | * Distributed under the Boost Software License, Version 1.0. |
| 4 | * (See accompanying file LICENSE_1_0.txt or copy at |
| 5 | * http://www.boost.org/LICENSE_1_0.txt) |
| 6 | */ |
| 7 | /*! |
| 8 | * \file attachable_sstream_buf.hpp |
| 9 | * \author Andrey Semashev |
| 10 | * \date 29.07.2007 |
| 11 | * |
| 12 | * \brief This header is the Boost.Log library implementation, see the library documentation |
| 13 | * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. |
| 14 | */ |
| 15 | |
| 16 | #ifndef BOOST_LOG_ATTACHABLE_SSTREAM_BUF_HPP_INCLUDED_ |
| 17 | #define BOOST_LOG_ATTACHABLE_SSTREAM_BUF_HPP_INCLUDED_ |
| 18 | |
| 19 | #include <cstddef> |
| 20 | #include <memory> |
| 21 | #include <locale> |
| 22 | #include <string> |
| 23 | #include <streambuf> |
| 24 | #include <boost/assert.hpp> |
| 25 | #include <boost/cstdint.hpp> |
| 26 | #include <boost/type_traits/integral_constant.hpp> |
| 27 | #include <boost/log/detail/config.hpp> |
| 28 | #include <boost/log/detail/header.hpp> |
| 29 | |
| 30 | #ifdef BOOST_HAS_PRAGMA_ONCE |
| 31 | #pragma once |
| 32 | #endif |
| 33 | |
| 34 | namespace boost { |
| 35 | |
| 36 | BOOST_LOG_OPEN_NAMESPACE |
| 37 | |
| 38 | namespace aux { |
| 39 | |
| 40 | //! A streambuf that puts the formatted data to an external string |
| 41 | template< |
| 42 | typename CharT, |
| 43 | typename TraitsT = std::char_traits< CharT >, |
| 44 | typename AllocatorT = std::allocator< CharT > |
| 45 | > |
| 46 | class basic_ostringstreambuf : |
| 47 | public std::basic_streambuf< CharT, TraitsT > |
| 48 | { |
| 49 | //! Self type |
| 50 | typedef basic_ostringstreambuf< CharT, TraitsT, AllocatorT > this_type; |
| 51 | //! Base type |
| 52 | typedef std::basic_streambuf< CharT, TraitsT > base_type; |
| 53 | |
| 54 | //! Buffer size |
| 55 | enum { buffer_size = 16 }; |
| 56 | |
| 57 | public: |
| 58 | //! Character type |
| 59 | typedef typename base_type::char_type char_type; |
| 60 | //! Traits type |
| 61 | typedef typename base_type::traits_type traits_type; |
| 62 | //! String type |
| 63 | typedef std::basic_string< char_type, traits_type, AllocatorT > string_type; |
| 64 | //! Size type |
| 65 | typedef typename string_type::size_type size_type; |
| 66 | //! Int type |
| 67 | typedef typename base_type::int_type int_type; |
| 68 | |
| 69 | struct storage_state |
| 70 | { |
| 71 | //! A reference to the string that will be filled |
| 72 | string_type* storage; |
| 73 | //! Max size of the storage, in characters |
| 74 | size_type max_size; |
| 75 | //! Indicates that storage overflow happened |
| 76 | bool overflow; |
| 77 | |
| 78 | BOOST_CONSTEXPR storage_state() BOOST_NOEXCEPT : storage(NULL), max_size(0u), overflow(false) |
| 79 | { |
| 80 | } |
| 81 | }; |
| 82 | |
| 83 | private: |
| 84 | //! Buffer storage state |
| 85 | storage_state m_storage_state; |
| 86 | //! A buffer used to temporarily store output |
| 87 | char_type m_buffer[buffer_size]; |
| 88 | |
| 89 | public: |
| 90 | //! Constructor |
| 91 | basic_ostringstreambuf() BOOST_NOEXCEPT |
| 92 | { |
| 93 | base_type::setp(m_buffer, m_buffer + (sizeof(m_buffer) / sizeof(*m_buffer))); |
| 94 | } |
| 95 | //! Constructor |
| 96 | explicit basic_ostringstreambuf(string_type& storage) BOOST_NOEXCEPT |
| 97 | { |
| 98 | base_type::setp(m_buffer, m_buffer + (sizeof(m_buffer) / sizeof(*m_buffer))); |
| 99 | attach(storage); |
| 100 | } |
| 101 | |
| 102 | storage_state const& get_storage_state() const BOOST_NOEXCEPT { return m_storage_state; } |
| 103 | void set_storage_state(storage_state const& st) BOOST_NOEXCEPT { m_storage_state = st; } |
| 104 | |
| 105 | //! Detaches the buffer from the string |
| 106 | void detach() |
| 107 | { |
| 108 | if (m_storage_state.storage) |
| 109 | { |
| 110 | this_type::sync(); |
| 111 | m_storage_state.storage = NULL; |
| 112 | m_storage_state.max_size = 0u; |
| 113 | m_storage_state.overflow = false; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | //! Attaches the buffer to another string |
| 118 | void attach(string_type& storage) |
| 119 | { |
| 120 | attach(storage, storage.max_size()); |
| 121 | } |
| 122 | |
| 123 | //! Attaches the buffer to another string |
| 124 | void attach(string_type& storage, size_type max_size) |
| 125 | { |
| 126 | detach(); |
| 127 | m_storage_state.storage = &storage; |
| 128 | this->max_size(max_size); |
| 129 | } |
| 130 | |
| 131 | //! Returns a pointer to the attached string |
| 132 | string_type* storage() const BOOST_NOEXCEPT { return m_storage_state.storage; } |
| 133 | |
| 134 | //! Returns the maximum size of the storage |
| 135 | size_type max_size() const BOOST_NOEXCEPT { return m_storage_state.max_size; } |
| 136 | //! Sets the maximum size of the storage |
| 137 | void max_size(size_type size) |
| 138 | { |
| 139 | if (m_storage_state.storage) |
| 140 | { |
| 141 | const size_type storage_max_size = m_storage_state.storage->max_size(); |
| 142 | size = size > storage_max_size ? storage_max_size : size; |
| 143 | } |
| 144 | |
| 145 | m_storage_state.max_size = size; |
| 146 | ensure_max_size(); |
| 147 | } |
| 148 | //! Makes sure the storage does not exceed the max size limit. Should be called after the storage is modified externally. |
| 149 | void ensure_max_size() |
| 150 | { |
| 151 | if (m_storage_state.storage && m_storage_state.storage->size() > m_storage_state.max_size) |
| 152 | { |
| 153 | const size_type len = length_until_boundary(m_storage_state.storage->c_str(), m_storage_state.storage->size(), m_storage_state.max_size); |
| 154 | m_storage_state.storage->resize(len); |
| 155 | m_storage_state.overflow = true; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | //! Returns true if the max size limit has been exceeded |
| 160 | bool storage_overflow() const BOOST_NOEXCEPT { return m_storage_state.overflow; } |
| 161 | //! Sets the overflow flag |
| 162 | void storage_overflow(bool f) BOOST_NOEXCEPT { m_storage_state.overflow = f; } |
| 163 | |
| 164 | //! Returns the size left in the storage |
| 165 | size_type size_left() const BOOST_NOEXCEPT |
| 166 | { |
| 167 | BOOST_ASSERT(m_storage_state.storage != NULL); |
| 168 | |
| 169 | const size_type size = m_storage_state.storage->size(); |
| 170 | return size < m_storage_state.max_size ? m_storage_state.max_size - size : static_cast< size_type >(0u); |
| 171 | } |
| 172 | |
| 173 | //! Appends a string to the storage and returns the number of written characters |
| 174 | size_type append(const char_type* s, size_type n) |
| 175 | { |
| 176 | if (!m_storage_state.overflow) |
| 177 | { |
| 178 | BOOST_ASSERT(m_storage_state.storage != NULL); |
| 179 | |
| 180 | size_type left = size_left(); |
| 181 | BOOST_LOG_ASSUME(left <= m_storage_state.storage->max_size()); |
| 182 | if (BOOST_LIKELY(n <= left)) |
| 183 | { |
| 184 | m_storage_state.storage->append(s, n); |
| 185 | return n; |
| 186 | } |
| 187 | else |
| 188 | { |
| 189 | // We have to find out where the last character that fits before the limit ends |
| 190 | left = length_until_boundary(s, n, left); |
| 191 | m_storage_state.storage->append(s, left); |
| 192 | m_storage_state.overflow = true; |
| 193 | return left; |
| 194 | } |
| 195 | } |
| 196 | return 0u; |
| 197 | } |
| 198 | |
| 199 | //! Appends the specified number of characters to the storage and returns the number of written characters |
| 200 | size_type append(size_type n, char_type c) |
| 201 | { |
| 202 | if (!m_storage_state.overflow) |
| 203 | { |
| 204 | BOOST_ASSERT(m_storage_state.storage != NULL); |
| 205 | |
| 206 | const size_type left = size_left(); |
| 207 | BOOST_LOG_ASSUME(left <= m_storage_state.storage->max_size()); |
| 208 | if (BOOST_LIKELY(n <= left)) |
| 209 | { |
| 210 | m_storage_state.storage->append(n, c); |
| 211 | return n; |
| 212 | } |
| 213 | else |
| 214 | { |
| 215 | m_storage_state.storage->append(left, c); |
| 216 | m_storage_state.overflow = true; |
| 217 | return left; |
| 218 | } |
| 219 | } |
| 220 | return 0u; |
| 221 | } |
| 222 | |
| 223 | //! Appends a character to the storage and returns the number of written characters |
| 224 | size_type push_back(char_type c) |
| 225 | { |
| 226 | if (!m_storage_state.overflow) |
| 227 | { |
| 228 | BOOST_ASSERT(m_storage_state.storage != NULL); |
| 229 | |
| 230 | BOOST_LOG_ASSUME(m_storage_state.max_size <= m_storage_state.storage->max_size()); |
| 231 | if (BOOST_LIKELY(m_storage_state.storage->size() < m_storage_state.max_size)) |
| 232 | { |
| 233 | m_storage_state.storage->push_back(c); |
| 234 | return 1u; |
| 235 | } |
| 236 | else |
| 237 | { |
| 238 | m_storage_state.overflow = true; |
| 239 | return 0u; |
| 240 | } |
| 241 | } |
| 242 | return 0u; |
| 243 | } |
| 244 | |
| 245 | protected: |
| 246 | //! Puts all buffered data to the string |
| 247 | int sync() BOOST_OVERRIDE |
| 248 | { |
| 249 | char_type* pBase = this->pbase(); |
| 250 | char_type* pPtr = this->pptr(); |
| 251 | if (pBase != pPtr) |
| 252 | { |
| 253 | this->append(pBase, static_cast< size_type >(pPtr - pBase)); |
| 254 | this->pbump(static_cast< int >(pBase - pPtr)); |
| 255 | } |
| 256 | return 0; |
| 257 | } |
| 258 | //! Puts an unbuffered character to the string |
| 259 | int_type overflow(int_type c) BOOST_OVERRIDE |
| 260 | { |
| 261 | this_type::sync(); |
| 262 | if (!traits_type::eq_int_type(c, traits_type::eof())) |
| 263 | { |
| 264 | this->push_back(c: traits_type::to_char_type(c)); |
| 265 | return c; |
| 266 | } |
| 267 | else |
| 268 | return traits_type::not_eof(c); |
| 269 | } |
| 270 | //! Puts a character sequence to the string |
| 271 | std::streamsize xsputn(const char_type* s, std::streamsize n) BOOST_OVERRIDE |
| 272 | { |
| 273 | this_type::sync(); |
| 274 | return static_cast< std::streamsize >(this->append(s, static_cast< size_type >(n))); |
| 275 | } |
| 276 | |
| 277 | //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size |
| 278 | size_type length_until_boundary(const char_type* s, size_type n, size_type max_size) const |
| 279 | { |
| 280 | BOOST_ASSERT(max_size <= n); |
| 281 | return length_until_boundary(s, n, max_size, boost::integral_constant< std::size_t, sizeof(char_type) >()); |
| 282 | } |
| 283 | |
| 284 | private: |
| 285 | //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size |
| 286 | size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::integral_constant< std::size_t, 1u >) const |
| 287 | { |
| 288 | std::locale loc = this->getloc(); |
| 289 | std::codecvt< wchar_t, char, std::mbstate_t > const& fac = std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc: loc); |
| 290 | std::mbstate_t mbs = std::mbstate_t(); |
| 291 | return static_cast< size_type >(fac.length(state&: mbs, from: s, end: s + max_size, max: n)); |
| 292 | } |
| 293 | |
| 294 | //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size |
| 295 | static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::integral_constant< std::size_t, 2u >) |
| 296 | { |
| 297 | // Note: Although it's not required to be true for wchar_t, here we assume that the string has Unicode encoding (UTF-16 or UCS-2). |
| 298 | // Compilers use some version of Unicode for wchar_t on all tested platforms, and std::locale doesn't offer a way |
| 299 | // to find the character boundary for character types other than char anyway. |
| 300 | size_type pos = max_size; |
| 301 | while (pos > 0u) |
| 302 | { |
| 303 | --pos; |
| 304 | uint_fast16_t c = static_cast< uint_fast16_t >(s[pos]); |
| 305 | // Check if this is a leading surrogate |
| 306 | if ((c & 0xFC00u) != 0xD800u) |
| 307 | return pos + 1u; |
| 308 | } |
| 309 | |
| 310 | return 0u; |
| 311 | } |
| 312 | |
| 313 | //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size |
| 314 | static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::integral_constant< std::size_t, 4u >) |
| 315 | { |
| 316 | // In UTF-32 and UCS-4 one code point is encoded as one code unit |
| 317 | return max_size; |
| 318 | } |
| 319 | |
| 320 | //! Copy constructor (closed) |
| 321 | BOOST_DELETED_FUNCTION(basic_ostringstreambuf(basic_ostringstreambuf const& that)) |
| 322 | //! Assignment (closed) |
| 323 | BOOST_DELETED_FUNCTION(basic_ostringstreambuf& operator= (basic_ostringstreambuf const& that)) |
| 324 | }; |
| 325 | |
| 326 | } // namespace aux |
| 327 | |
| 328 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
| 329 | |
| 330 | } // namespace boost |
| 331 | |
| 332 | #include <boost/log/detail/footer.hpp> |
| 333 | |
| 334 | #endif // BOOST_LOG_ATTACHABLE_SSTREAM_BUF_HPP_INCLUDED_ |
| 335 | |