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/mpl/bool.hpp>
26 #include <boost/locale/utf.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 
storage_stateboost::aux::basic_ostringstreambuf::storage_state78         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
basic_ostringstreambuf()91     basic_ostringstreambuf() BOOST_NOEXCEPT
92     {
93         base_type::setp(m_buffer, m_buffer + (sizeof(m_buffer) / sizeof(*m_buffer)));
94     }
95     //! Constructor
basic_ostringstreambuf(string_type & storage)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 
get_storage_state() const102     storage_state const& get_storage_state() const BOOST_NOEXCEPT { return m_storage_state; }
set_storage_state(storage_state const & st)103     void set_storage_state(storage_state const& st) BOOST_NOEXCEPT { m_storage_state = st; }
104 
105     //! Detaches the buffer from the string
detach()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
attach(string_type & storage)118     void attach(string_type& storage)
119     {
120         attach(storage, storage.max_size());
121     }
122 
123     //! Attaches the buffer to another string
attach(string_type & storage,size_type max_size)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
storage() const132     string_type* storage() const BOOST_NOEXCEPT { return m_storage_state.storage; }
133 
134     //! Returns the maximum size of the storage
max_size() const135     size_type max_size() const BOOST_NOEXCEPT { return m_storage_state.max_size; }
136     //! Sets the maximum size of the storage
max_size(size_type size)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.
ensure_max_size()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
storage_overflow() const160     bool storage_overflow() const BOOST_NOEXCEPT { return m_storage_state.overflow; }
161     //! Sets the overflow flag
storage_overflow(bool f)162     void storage_overflow(bool f) BOOST_NOEXCEPT { m_storage_state.overflow = f; }
163 
164     //! Returns the size left in the storage
size_left() const165     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
append(const char_type * s,size_type n)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
append(size_type n,char_type c)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
push_back(char_type c)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
sync()247     int sync()
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
overflow(int_type c)259     int_type overflow(int_type c)
260     {
261         this_type::sync();
262         if (!traits_type::eq_int_type(c, traits_type::eof()))
263         {
264             this->push_back(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
xsputn(const char_type * s,std::streamsize n)271     std::streamsize xsputn(const char_type* s, std::streamsize n)
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
length_until_boundary(const char_type * s,size_type n,size_type max_size) const278     size_type length_until_boundary(const char_type* s, size_type n, size_type max_size) const
279     {
280         return length_until_boundary(s, n, max_size, mpl::bool_< sizeof(char_type) == 1u >());;
281     }
282 
283     //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size
length_until_boundary(const char_type * s,size_type n,size_type max_size,mpl::true_) const284     size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, mpl::true_) const
285     {
286         std::locale loc = this->getloc();
287         std::codecvt< wchar_t, char, std::mbstate_t > const& fac = std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc);
288         std::mbstate_t mbs = std::mbstate_t();
289         return static_cast< size_type >(fac.length(mbs, s, s + max_size, ~static_cast< std::size_t >(0u)));
290     }
291 
292     //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size
length_until_boundary(const char_type * s,size_type n,size_type max_size,mpl::false_)293     static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, mpl::false_)
294     {
295         // Note: Although it's not required to be true for wchar_t, here we assume that the string has Unicode encoding.
296         // Compilers use some version of Unicode for wchar_t on all tested platforms, and std::locale doesn't offer a way
297         // to find the character boundary for character types other than char anyway.
298         typedef boost::locale::utf::utf_traits< CharT > utf_traits;
299 
300         size_type pos = max_size;
301         while (pos > 0u)
302         {
303             --pos;
304             if (utf_traits::is_lead(s[pos]))
305             {
306                 const char_type* p = s + pos;
307                 boost::locale::utf::code_point cp = utf_traits::decode(p, s + n);
308                 if (boost::locale::utf::is_valid_codepoint(cp) && p <= (s + max_size))
309                     return static_cast< size_type >(p - s);
310             }
311         }
312 
313         return 0u;
314     }
315 
316     //! Copy constructor (closed)
317     BOOST_DELETED_FUNCTION(basic_ostringstreambuf(basic_ostringstreambuf const& that))
318     //! Assignment (closed)
319     BOOST_DELETED_FUNCTION(basic_ostringstreambuf& operator= (basic_ostringstreambuf const& that))
320 };
321 
322 } // namespace aux
323 
324 BOOST_LOG_CLOSE_NAMESPACE // namespace log
325 
326 } // namespace boost
327 
328 #include <boost/log/detail/footer.hpp>
329 
330 #endif // BOOST_LOG_ATTACHABLE_SSTREAM_BUF_HPP_INCLUDED_
331