]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
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() | |
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) | |
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 | |
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 | |
278 | size_type length_until_boundary(const char_type* s, size_type n, size_type max_size) const | |
279 | { | |
b32b8144 | 280 | return length_until_boundary(s, n, max_size, mpl::bool_< sizeof(char_type) == 1u >()); |
7c673cae FG |
281 | } |
282 | ||
283 | //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size | |
284 | 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 | |
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. | |
b32b8144 | 298 | typedef boost::locale::utf::utf_traits< char_type > utf_traits; |
7c673cae FG |
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_ |