1 /*
2  *             Copyright Andrey Semashev 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   formatters/max_size_decorator.hpp
9  * \author Andrey Semashev
10  * \date   06.07.2016
11  *
12  * The header contains implementation of a string length limiting decorator.
13  */
14 
15 #ifndef BOOST_LOG_EXPRESSIONS_FORMATTERS_MAX_SIZE_DECORATOR_HPP_INCLUDED_
16 #define BOOST_LOG_EXPRESSIONS_FORMATTERS_MAX_SIZE_DECORATOR_HPP_INCLUDED_
17 
18 #include <cstddef>
19 #include <string>
20 #include <boost/assert.hpp>
21 #include <boost/mpl/bool.hpp>
22 #include <boost/move/core.hpp>
23 #include <boost/move/utility_core.hpp>
24 #include <boost/core/addressof.hpp>
25 #include <boost/phoenix/core/actor.hpp>
26 #include <boost/phoenix/core/meta_grammar.hpp>
27 #include <boost/phoenix/core/terminal_fwd.hpp>
28 #include <boost/phoenix/core/is_nullary.hpp>
29 #include <boost/phoenix/core/environment.hpp>
30 #include <boost/phoenix/support/vector.hpp>
31 #include <boost/fusion/sequence/intrinsic/at_c.hpp>
32 #include <boost/type_traits/remove_cv.hpp>
33 #include <boost/type_traits/remove_reference.hpp>
34 #include <boost/log/detail/config.hpp>
35 #include <boost/log/detail/custom_terminal_spec.hpp>
36 #include <boost/log/utility/formatting_ostream.hpp>
37 #include <boost/log/detail/header.hpp>
38 
39 #ifdef BOOST_HAS_PRAGMA_ONCE
40 #pragma once
41 #endif
42 
43 namespace boost {
44 
45 BOOST_LOG_OPEN_NAMESPACE
46 
47 namespace expressions {
48 
49 namespace aux {
50 
51 //! String size limiting decorator stream output terminal
52 template< typename LeftT, typename SubactorT, typename CharT >
53 class max_size_decorator_output_terminal
54 {
55 private:
56     //! Self type
57     typedef max_size_decorator_output_terminal< LeftT, SubactorT, CharT > this_type;
58 
59 public:
60 #ifndef BOOST_LOG_DOXYGEN_PASS
61     //! Internal typedef for type categorization
62     typedef void _is_boost_log_terminal;
63 #endif
64 
65     //! Character type
66     typedef CharT char_type;
67     //! String type
68     typedef std::basic_string< char_type > string_type;
69     //! String size type
70     typedef std::size_t size_type;
71     //! Adopted actor type
72     typedef SubactorT subactor_type;
73 
74     //! Result type definition
75     template< typename >
76     struct result;
77 
78     template< typename ThisT, typename ContextT >
79     struct result< ThisT(ContextT) >
80     {
81         typedef typename remove_cv< typename remove_reference< ContextT >::type >::type context_type;
82         typedef typename phoenix::evaluator::impl<
83             typename LeftT::proto_base_expr&,
84             context_type,
85             phoenix::unused
86         >::result_type type;
87     };
88 
89 private:
90     //! Left argument actor
91     LeftT m_left;
92     //! Adopted formatter actor
93     subactor_type m_subactor;
94     //! Max size of the formatted string produced by the adopted formatter
95     size_type m_max_size;
96     //! Overflow marker
97     string_type m_overflow_marker;
98 
99 public:
100     /*!
101      * Initializing constructor. Creates decorator of the \a fmt formatter with the specified \a decorations.
102      */
max_size_decorator_output_terminal(LeftT const & left,subactor_type const & sub,size_type max_size,string_type const & overflow_marker=string_type ())103     max_size_decorator_output_terminal(LeftT const& left, subactor_type const& sub, size_type max_size, string_type const& overflow_marker = string_type()) :
104         m_left(left), m_subactor(sub), m_max_size(max_size), m_overflow_marker(overflow_marker)
105     {
106         BOOST_ASSERT(overflow_marker.size() <= max_size);
107     }
108     /*!
109      * Copy constructor
110      */
max_size_decorator_output_terminal(max_size_decorator_output_terminal const & that)111     max_size_decorator_output_terminal(max_size_decorator_output_terminal const& that) :
112         m_left(that.m_left), m_subactor(that.m_subactor), m_max_size(that.m_max_size), m_overflow_marker(that.m_overflow_marker)
113     {
114     }
115 
116     /*!
117      * Invokation operator
118      */
119     template< typename ContextT >
operator ()(ContextT const & ctx)120     typename result< this_type(ContextT const&) >::type operator() (ContextT const& ctx)
121     {
122         // Flush the stream and keep the current write position in the target string
123         typedef typename result< this_type(ContextT const&) >::type result_type;
124         result_type strm = phoenix::eval(m_left, ctx);
125         strm.flush();
126 
127         if (!strm.rdbuf()->storage_overflow())
128         {
129             const size_type old_max_size = strm.rdbuf()->max_size();
130             const size_type start_pos = strm.rdbuf()->storage()->size(), max_size_left = strm.rdbuf()->storage()->max_size() - start_pos;
131             strm.rdbuf()->max_size(start_pos + (m_max_size <= max_size_left ? m_max_size : max_size_left));
132 
133             try
134             {
135                 // Invoke the adopted formatter
136                 phoenix::eval(m_subactor, ctx);
137 
138                 // Flush the buffered characters and apply decorations
139                 strm.flush();
140 
141                 if (strm.rdbuf()->storage_overflow())
142                 {
143                     if (!m_overflow_marker.empty())
144                     {
145                         // Free up space for the overflow marker
146                         strm.rdbuf()->max_size(strm.rdbuf()->max_size() - m_overflow_marker.size());
147 
148                         // Append the marker
149                         strm.rdbuf()->max_size(strm.rdbuf()->storage()->max_size());
150                         strm.rdbuf()->storage_overflow(false);
151                         strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size());
152                     }
153                     else
154                     {
155                         strm.rdbuf()->storage_overflow(false);
156                     }
157                 }
158 
159                 // Restore the original size limit
160                 strm.rdbuf()->max_size(old_max_size);
161             }
162             catch (...)
163             {
164                 strm.rdbuf()->storage_overflow(false);
165                 strm.rdbuf()->max_size(old_max_size);
166                 throw;
167             }
168         }
169 
170         return strm;
171     }
172 
173     /*!
174      * Invokation operator
175      */
176     template< typename ContextT >
operator ()(ContextT const & ctx) const177     typename result< const this_type(ContextT const&) >::type operator() (ContextT const& ctx) const
178     {
179         // Flush the stream and keep the current write position in the target string
180         typedef typename result< const this_type(ContextT const&) >::type result_type;
181         result_type strm = phoenix::eval(m_left, ctx);
182         strm.flush();
183 
184         if (!strm.rdbuf()->storage_overflow())
185         {
186             const size_type old_max_size = strm.rdbuf()->max_size();
187             const size_type start_pos = strm.rdbuf()->storage()->size(), max_size_left = strm.rdbuf()->storage()->max_size() - start_pos;
188             strm.rdbuf()->max_size(start_pos + (m_max_size <= max_size_left ? m_max_size : max_size_left));
189 
190             try
191             {
192                 // Invoke the adopted formatter
193                 phoenix::eval(m_subactor, ctx);
194 
195                 // Flush the buffered characters and apply decorations
196                 strm.flush();
197 
198                 if (strm.rdbuf()->storage_overflow())
199                 {
200                     if (!m_overflow_marker.empty())
201                     {
202                         // Free up space for the overflow marker
203                         strm.rdbuf()->max_size(strm.rdbuf()->max_size() - m_overflow_marker.size());
204 
205                         // Append the marker
206                         strm.rdbuf()->max_size(strm.rdbuf()->storage()->max_size());
207                         strm.rdbuf()->storage_overflow(false);
208                         strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size());
209                     }
210                     else
211                     {
212                         strm.rdbuf()->storage_overflow(false);
213                     }
214                 }
215 
216                 // Restore the original size limit
217                 strm.rdbuf()->max_size(old_max_size);
218             }
219             catch (...)
220             {
221                 strm.rdbuf()->storage_overflow(false);
222                 strm.rdbuf()->max_size(old_max_size);
223                 throw;
224             }
225         }
226 
227         return strm;
228     }
229 
230     BOOST_DELETED_FUNCTION(max_size_decorator_output_terminal())
231 };
232 
233 } // namespace aux
234 
235 /*!
236  * String size limiting decorator terminal class. This formatter allows to limit the maximum total length
237  * of the strings generated by other formatters.
238  *
239  * The \c max_size_decorator_terminal class aggregates the formatter being decorated, the maximum string length
240  * it can produce and an optional truncation marker string, which will be put at the end of the output if the limit is exceeded. Note that
241  * the marker length is included in the limit and as such must not exceed it.
242  * The \c max_size_decorator_terminal class is a formatter itself, so it can be used to construct
243  * more complex formatters, including nesting decorators.
244  */
245 template< typename SubactorT, typename CharT >
246 class max_size_decorator_terminal
247 {
248 private:
249     //! Self type
250     typedef max_size_decorator_terminal< SubactorT, CharT > this_type;
251 
252 public:
253 #ifndef BOOST_LOG_DOXYGEN_PASS
254     //! Internal typedef for type categorization
255     typedef void _is_boost_log_terminal;
256 #endif
257 
258     //! Character type
259     typedef CharT char_type;
260     //! String type
261     typedef std::basic_string< char_type > string_type;
262     //! String size type
263     typedef std::size_t size_type;
264     //! Stream type
265     typedef basic_formatting_ostream< char_type > stream_type;
266     //! Adopted actor type
267     typedef SubactorT subactor_type;
268 
269     //! Result type definition
270     typedef string_type result_type;
271 
272 private:
273     //! Adopted formatter actor
274     subactor_type m_subactor;
275     //! Max size of the formatted string produced by the adopted formatter
276     size_type m_max_size;
277     //! Overflow marker
278     string_type m_overflow_marker;
279 
280 public:
281     /*!
282      * Initializing constructor.
283      */
max_size_decorator_terminal(subactor_type const & sub,size_type max_size,string_type const & overflow_marker=string_type ())284     max_size_decorator_terminal(subactor_type const& sub, size_type max_size, string_type const& overflow_marker = string_type()) :
285         m_subactor(sub), m_max_size(max_size), m_overflow_marker(overflow_marker)
286     {
287         BOOST_ASSERT(overflow_marker.size() <= max_size);
288     }
289     /*!
290      * Copy constructor
291      */
max_size_decorator_terminal(max_size_decorator_terminal const & that)292     max_size_decorator_terminal(max_size_decorator_terminal const& that) :
293         m_subactor(that.m_subactor), m_max_size(that.m_max_size), m_overflow_marker(that.m_overflow_marker)
294     {
295     }
296 
297     /*!
298      * \returns Adopted subactor
299      */
get_subactor() const300     subactor_type const& get_subactor() const
301     {
302         return m_subactor;
303     }
304 
305     /*!
306      * \returns Max string size limit
307      */
get_max_size() const308     size_type get_max_size() const
309     {
310         return m_max_size;
311     }
312 
313     /*!
314      * \returns Max string size limit
315      */
get_overflow_marker() const316     string_type const& get_overflow_marker() const
317     {
318         return m_overflow_marker;
319     }
320 
321     /*!
322      * Invokation operator
323      */
324     template< typename ContextT >
operator ()(ContextT const & ctx)325     result_type operator() (ContextT const& ctx)
326     {
327         string_type str;
328         stream_type strm(str);
329         strm.rdbuf()->max_size(m_max_size);
330 
331         // Invoke the adopted formatter
332         typedef phoenix::vector3<
333             subactor_type*,
334             typename fusion::result_of::at_c<
335                 typename remove_cv<
336                     typename remove_reference<
337                         typename phoenix::result_of::env< ContextT const& >::type
338                     >::type
339                 >::type::args_type,
340                 0
341             >::type,
342             stream_type&
343         > env_type;
344         env_type env = { boost::addressof(m_subactor), fusion::at_c< 0 >(phoenix::env(ctx).args()), strm };
345         phoenix::eval(m_subactor, phoenix::make_context(env, phoenix::actions(ctx)));
346 
347         // Flush the buffered characters and see of overflow happened
348         strm.flush();
349 
350         if (strm.rdbuf()->storage_overflow() && !m_overflow_marker.empty())
351         {
352             strm.rdbuf()->max_size(strm.rdbuf()->max_size() - m_overflow_marker.size());
353             strm.rdbuf()->max_size(str.max_size());
354             strm.rdbuf()->storage_overflow(false);
355             strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size());
356             strm.flush();
357         }
358 
359         return BOOST_LOG_NRVO_RESULT(str);
360     }
361 
362     /*!
363      * Invokation operator
364      */
365     template< typename ContextT >
operator ()(ContextT const & ctx) const366     result_type operator() (ContextT const& ctx) const
367     {
368         string_type str;
369         stream_type strm(str);
370         strm.rdbuf()->max_size(m_max_size);
371 
372         // Invoke the adopted formatter
373         typedef phoenix::vector3<
374             const subactor_type*,
375             typename fusion::result_of::at_c<
376                 typename remove_cv<
377                     typename remove_reference<
378                         typename phoenix::result_of::env< ContextT const& >::type
379                     >::type
380                 >::type::args_type,
381                 0
382             >::type,
383             stream_type&
384         > env_type;
385         env_type env = { boost::addressof(m_subactor), fusion::at_c< 0 >(phoenix::env(ctx).args()), strm };
386         phoenix::eval(m_subactor, phoenix::make_context(env, phoenix::actions(ctx)));
387 
388         // Flush the buffered characters and see of overflow happened
389         strm.flush();
390 
391         if (strm.rdbuf()->storage_overflow())
392         {
393             strm.rdbuf()->max_size(strm.rdbuf()->max_size() - m_overflow_marker.size());
394             strm.rdbuf()->max_size(str.max_size());
395             strm.rdbuf()->storage_overflow(false);
396             strm.rdbuf()->append(m_overflow_marker.data(), m_overflow_marker.size());
397             strm.flush();
398         }
399 
400         return BOOST_LOG_NRVO_RESULT(str);
401     }
402 
403     BOOST_DELETED_FUNCTION(max_size_decorator_terminal())
404 };
405 
406 /*!
407  * Character decorator actor
408  */
409 template< typename SubactorT, typename CharT, template< typename > class ActorT = phoenix::actor >
410 class max_size_decorator_actor :
411     public ActorT< max_size_decorator_terminal< SubactorT, CharT > >
412 {
413 public:
414     //! Base terminal type
415     typedef max_size_decorator_terminal< SubactorT, CharT > terminal_type;
416     //! Character type
417     typedef typename terminal_type::char_type char_type;
418 
419     //! Base actor type
420     typedef ActorT< terminal_type > base_type;
421 
422 public:
423     //! Initializing constructor
max_size_decorator_actor(base_type const & act)424     explicit max_size_decorator_actor(base_type const& act) : base_type(act)
425     {
426     }
427 
428     //! Returns reference to the terminal
get_terminal() const429     terminal_type const& get_terminal() const
430     {
431         return this->proto_expr_.child0;
432     }
433 };
434 
435 #ifndef BOOST_LOG_DOXYGEN_PASS
436 
437 #define BOOST_LOG_AUX_OVERLOAD(left_ref, right_ref)\
438     template< typename LeftExprT, typename SubactorT, typename CharT, template< typename > class ActorT >\
439     BOOST_FORCEINLINE phoenix::actor< aux::max_size_decorator_output_terminal< phoenix::actor< LeftExprT >, SubactorT, CharT > >\
440     operator<< (phoenix::actor< LeftExprT > left_ref left, max_size_decorator_actor< SubactorT, CharT, ActorT > right_ref right)\
441     {\
442         typedef aux::max_size_decorator_output_terminal< phoenix::actor< LeftExprT >, SubactorT, CharT > terminal_type;\
443         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()) }};\
444         return actor;\
445     }
446 
447 #include <boost/log/detail/generate_overloads.hpp>
448 
449 #undef BOOST_LOG_AUX_OVERLOAD
450 
451 #endif // BOOST_LOG_DOXYGEN_PASS
452 
453 namespace aux {
454 
455 template< typename CharT >
456 class max_size_decorator_gen
457 {
458 private:
459     typedef CharT char_type;
460     typedef std::basic_string< char_type > string_type;
461     typedef std::size_t size_type;
462 
463 private:
464     size_type m_max_size;
465     string_type m_overflow_marker;
466 
467 public:
max_size_decorator_gen(size_type max_size,string_type const & overflow_marker=string_type ())468     explicit max_size_decorator_gen(size_type max_size, string_type const& overflow_marker = string_type()) :
469         m_max_size(max_size), m_overflow_marker(overflow_marker)
470     {
471         BOOST_ASSERT(overflow_marker.size() <= max_size);
472     }
473 
474     template< typename SubactorT >
operator [](SubactorT const & subactor) const475     BOOST_FORCEINLINE max_size_decorator_actor< SubactorT, char_type > operator[] (SubactorT const& subactor) const
476     {
477         typedef max_size_decorator_actor< SubactorT, char_type > result_type;
478         typedef typename result_type::terminal_type terminal_type;
479         typename result_type::base_type act = {{ terminal_type(subactor, m_max_size, m_overflow_marker) }};
480         return result_type(act);
481     }
482 };
483 
484 } // namespace aux
485 
486 /*!
487  * The function returns a decorator generator object. The generator provides <tt>operator[]</tt> that can be used
488  * to construct the actual decorator.
489  *
490  * \param max_size The maximum number of characters (i.e. string element objects) that the decorated formatter can produce.
491  */
492 template< typename CharT >
max_size_decor(std::size_t max_size)493 BOOST_FORCEINLINE aux::max_size_decorator_gen< CharT > max_size_decor(std::size_t max_size)
494 {
495     return aux::max_size_decorator_gen< CharT >(max_size);
496 }
497 
498 /*!
499  * The function returns a decorator generator object. The generator provides <tt>operator[]</tt> that can be used
500  * to construct the actual decorator.
501  *
502  * \param max_size The maximum number of characters (i.e. string element objects) that the decorated formatter can produce.
503  * \param overflow_marker The marker string which is appended to the output if the \a max_size limit is exceeded. Must be
504  *                        a non-null pointer to a zero-terminated string.
505  *
506  * \pre The \a overflow_marker length must not exceed the \a max_size limit.
507  */
508 template< typename CharT >
max_size_decor(std::size_t max_size,const CharT * overflow_marker)509 BOOST_FORCEINLINE aux::max_size_decorator_gen< CharT > max_size_decor(std::size_t max_size, const CharT* overflow_marker)
510 {
511     return aux::max_size_decorator_gen< CharT >(max_size, overflow_marker);
512 }
513 
514 /*!
515  * The function returns a decorator generator object. The generator provides <tt>operator[]</tt> that can be used
516  * to construct the actual decorator.
517  *
518  * \param max_size The maximum number of characters (i.e. string element objects) that the decorated formatter can produce.
519  * \param overflow_marker The marker string which is appended to the output if the \a max_size limit is exceeded.
520  *
521  * \pre The \a overflow_marker length must not exceed the \a max_size limit.
522  */
523 template< typename CharT >
max_size_decor(std::size_t max_size,std::basic_string<CharT> const & overflow_marker)524 BOOST_FORCEINLINE aux::max_size_decorator_gen< CharT > max_size_decor(std::size_t max_size, std::basic_string< CharT > const& overflow_marker)
525 {
526     return aux::max_size_decorator_gen< CharT >(max_size, overflow_marker);
527 }
528 
529 } // namespace expressions
530 
531 BOOST_LOG_CLOSE_NAMESPACE // namespace log
532 
533 #ifndef BOOST_LOG_DOXYGEN_PASS
534 
535 namespace phoenix {
536 
537 namespace result_of {
538 
539 template< typename SubactorT, typename CharT >
540 struct is_nullary< custom_terminal< boost::log::expressions::max_size_decorator_terminal< SubactorT, CharT > > > :
541     public mpl::false_
542 {
543 };
544 
545 template< typename LeftT, typename SubactorT, typename CharT >
546 struct is_nullary< custom_terminal< boost::log::expressions::aux::max_size_decorator_output_terminal< LeftT, SubactorT, CharT > > > :
547     public mpl::false_
548 {
549 };
550 
551 } // namespace result_of
552 
553 } // namespace phoenix
554 
555 #endif
556 
557 } // namespace boost
558 
559 #include <boost/log/detail/footer.hpp>
560 
561 #endif // BOOST_LOG_EXPRESSIONS_FORMATTERS_MAX_SIZE_DECORATOR_HPP_INCLUDED_
562