attachable_sstream_buf.hpp 11 KB

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