Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r84126 - in trunk: boost/log/utility boost/log/utility/manipulators libs/log/build libs/log/doc libs/log/src libs/log/test/run
From: andrey.semashev_at_[hidden]
Date: 2013-05-03 14:19:33


Author: andysem
Date: 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
New Revision: 84126
URL: http://svn.boost.org/trac/boost/changeset/84126

Log:
Added a new dump manipulator.
Added:
   trunk/boost/log/utility/manipulators/dump.hpp (contents, props changed)
   trunk/libs/log/src/dump.cpp (contents, props changed)
   trunk/libs/log/test/run/util_manip_dump.cpp (contents, props changed)
Text files modified:
   trunk/boost/log/utility/manipulators.hpp | 2 +-
   trunk/libs/log/build/Jamfile.v2 | 1 +
   trunk/libs/log/doc/changelog.qbk | 1 +
   trunk/libs/log/doc/utilities.qbk | 32 ++++++++++++++++++++++++++++++++
   4 files changed, 35 insertions(+), 1 deletions(-)

Modified: trunk/boost/log/utility/manipulators.hpp
==============================================================================
--- trunk/boost/log/utility/manipulators.hpp (original)
+++ trunk/boost/log/utility/manipulators.hpp 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -18,8 +18,8 @@
 #include <boost/log/detail/config.hpp>
 
 #include <boost/log/utility/manipulators/add_value.hpp>
-
 #include <boost/log/utility/manipulators/to_log.hpp>
+#include <boost/log/utility/manipulators/dump.hpp>
 
 #ifdef BOOST_LOG_HAS_PRAGMA_ONCE
 #pragma once

Added: trunk/boost/log/utility/manipulators/dump.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/log/utility/manipulators/dump.hpp 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -0,0 +1,202 @@
+/*
+ * Copyright Andrey Semashev 2007 - 2013.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+/*!
+ * \file dump.hpp
+ * \author Andrey Semashev
+ * \date 03.05.2013
+ *
+ * This header contains the \c dump output manipulator.
+ */
+
+#ifndef BOOST_LOG_UTILITY_MANIPULATORS_DUMP_HPP_INCLUDED_
+#define BOOST_LOG_UTILITY_MANIPULATORS_DUMP_HPP_INCLUDED_
+
+#include <cstddef>
+#include <iosfwd>
+#include <boost/log/detail/config.hpp>
+#include <boost/log/detail/header.hpp>
+
+#ifdef BOOST_LOG_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+
+BOOST_LOG_OPEN_NAMESPACE
+
+namespace aux {
+
+template< typename CharT >
+BOOST_LOG_API void dump_data(const void* data, std::size_t size, std::basic_ostream< CharT >& strm);
+
+template< std::size_t SizeV, typename R >
+struct enable_dump_size_based
+{
+};
+
+template< typename R >
+struct enable_dump_size_based< 1u, R >
+{
+ typedef R type;
+};
+
+template< typename T, typename R >
+struct enable_dump :
+ public enable_dump_size_based< sizeof(T), R >
+{
+};
+
+template< typename R >
+struct enable_dump< void, R >
+{
+ typedef R type;
+};
+
+template< typename R >
+struct enable_dump< const void, R >
+{
+ typedef R type;
+};
+
+template< typename R >
+struct enable_dump< volatile void, R >
+{
+ typedef R type;
+};
+
+template< typename R >
+struct enable_dump< const volatile void, R >
+{
+ typedef R type;
+};
+
+} // namespace aux
+
+/*!
+ * \brief Manipulator for printing binary representation of the data
+ */
+class dump_manip
+{
+private:
+ //! Beginning of the data
+ const void* m_data;
+ //! Size of the data, in bytes
+ std::size_t m_size;
+
+public:
+ dump_manip(const void* data, std::size_t size) BOOST_NOEXCEPT : m_data(data), m_size(size) {}
+ dump_manip(dump_manip const& that) BOOST_NOEXCEPT : m_data(that.m_data), m_size(that.m_size) {}
+
+ const void* get_data() const BOOST_NOEXCEPT { return m_data; }
+ std::size_t get_size() const BOOST_NOEXCEPT { return m_size; }
+};
+
+//! The operator outputs binary data to a stream
+template< typename CharT, typename TraitsT >
+inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, dump_manip const& manip)
+{
+ if (strm.good())
+ aux::dump_data(manip.get_data(), manip.get_size(), strm);
+
+ return strm;
+}
+
+/*!
+ * \brief Manipulator for printing binary representation of the data with a size limit
+ */
+class bounded_dump_manip :
+ public dump_manip
+{
+private:
+ //! Maximum size to output, in bytes
+ std::size_t m_max_size;
+
+public:
+ bounded_dump_manip(const void* data, std::size_t size, std::size_t max_size) BOOST_NOEXCEPT : dump_manip(data, size), m_max_size(max_size) {}
+ bounded_dump_manip(bounded_dump_manip const& that) BOOST_NOEXCEPT : dump_manip(static_cast< dump_manip const& >(that)), m_max_size(that.m_max_size) {}
+
+ std::size_t get_max_size() const BOOST_NOEXCEPT { return m_max_size; }
+};
+
+//! The operator outputs binary data to a stream
+template< typename CharT, typename TraitsT >
+inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, bounded_dump_manip const& manip)
+{
+ if (strm.good())
+ {
+ const std::size_t size = manip.get_size(), max_size = manip.get_max_size();
+ if (max_size >= size)
+ {
+ aux::dump_data(manip.get_data(), size, strm);
+ }
+ else
+ {
+ aux::dump_data(manip.get_data(), max_size, strm);
+ strm << " and " << (size - max_size) << " bytes more";
+ }
+ }
+
+ return strm;
+}
+
+/*!
+ * \brief Creates a stream manipulator that will output contents of a memory region in hexadecimal form
+ * \param data The pointer to the beginning of the region
+ * \param size The size of the region, in bytes
+ * \return The manipulator that is to be put to a stream
+ */
+template< typename T >
+inline typename aux::enable_dump< T, dump_manip >::type dump(T* data, std::size_t size) BOOST_NOEXCEPT
+{
+ return dump_manip((const void*)data, size);
+}
+
+/*!
+ * \brief Creates a stream manipulator that will dump elements of an array in hexadecimal form
+ * \param data The pointer to the beginning of the array
+ * \param count The size of the region, in number of \c T elements
+ * \return The manipulator that is to be put to a stream
+ */
+template< typename T >
+inline dump_manip dump_elements(T* data, std::size_t count) BOOST_NOEXCEPT
+{
+ return dump_manip((const void*)data, count * sizeof(T));
+}
+
+/*!
+ * \brief Creates a stream manipulator that will output contents of a memory region in hexadecimal form
+ * \param data The pointer to the beginning of the region
+ * \param size The size of the region, in bytes
+ * \params max_size The maximum number of bytes of the region to output
+ * \return The manipulator that is to be put to a stream
+ */
+template< typename T >
+inline typename aux::enable_dump< T, bounded_dump_manip >::type dump(T* data, std::size_t size, std::size_t max_size) BOOST_NOEXCEPT
+{
+ return bounded_dump_manip((const void*)data, size, max_size);
+}
+
+/*!
+ * \brief Creates a stream manipulator that will dump elements of an array in hexadecimal form
+ * \param data The pointer to the beginning of the array
+ * \param count The size of the region, in number of \c T elements
+ * \params max_count The maximum number of elements to output
+ * \return The manipulator that is to be put to a stream
+ */
+template< typename T >
+inline bounded_dump_manip dump_elements(T* data, std::size_t count, std::size_t max_count) BOOST_NOEXCEPT
+{
+ return bounded_dump_manip((const void*)data, count * sizeof(T), max_count * sizeof(T));
+}
+
+BOOST_LOG_CLOSE_NAMESPACE // namespace log
+
+} // namespace boost
+
+#include <boost/log/detail/footer.hpp>
+
+#endif // BOOST_LOG_UTILITY_MANIPULATORS_DUMP_HPP_INCLUDED_

Modified: trunk/libs/log/build/Jamfile.v2
==============================================================================
--- trunk/libs/log/build/Jamfile.v2 (original)
+++ trunk/libs/log/build/Jamfile.v2 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -92,6 +92,7 @@
     date_time_format_parser.cpp
     named_scope_format_parser.cpp
     unhandled_exception_count.cpp
+ dump.cpp
     ;
 
 lib boost_log

Modified: trunk/libs/log/doc/changelog.qbk
==============================================================================
--- trunk/libs/log/doc/changelog.qbk (original)
+++ trunk/libs/log/doc/changelog.qbk 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -18,6 +18,7 @@
 [*General changes:]
 
 * Removed the use of deprecated macros of __boost_config__.
+* Added a new [link log.detailed.utilities.manipulators.dump `dump`] output manipulator for printing binary data.
 
 [heading 2.0, 13 April 2013]
 

Modified: trunk/libs/log/doc/utilities.qbk
==============================================================================
--- trunk/libs/log/doc/utilities.qbk (original)
+++ trunk/libs/log/doc/utilities.qbk 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -241,6 +241,38 @@
 
 [endsect]
 
+[section:dump Binary dump manipulator]
+
+ #include <``[boost_log_utility_manipulators_dump_hpp]``>
+
+The [funcref boost::log::dump `dump`] function creates a manipulator that outputs binary contents of a contiguous memory region. This can be useful for logging some low level binary data, such as encoded network packets or entries of a binary file. The use is quite straightforward:
+
+ void on_receive(std::vector< unsigned char > const& packet)
+ {
+ // Outputs something like "Packet received: 00 01 02 0a 0b 0c"
+ BOOST_LOG(lg) << "Packet received: " << logging::dump(packet.data(), packet.size());
+ }
+
+The manipulator also allows to limit the amount of data to be output, in case if the input data can be too large. Just specify the maximum number of bytes of input to dump as the last argument:
+
+ void on_receive(std::vector< unsigned char > const& packet)
+ {
+ // Outputs something like "Packet received: 00 01 02 03 04 05 06 07 08 and 67 bytes more"
+ BOOST_LOG(lg) << "Packet received: " << logging::dump(packet.data(), packet.size(), 8);
+ }
+
+There is another manipulator called [funcref boost::log::dump_elements `dump_elements`] for printing binary representation of non-byte array elements. The special manipulator for this case is necessary because the units of the size argument of [funcref boost::log::dump `dump`] can be confusing (is it in bytes or in elements?). Therefore [funcref boost::log::dump `dump`] will not compile when used for non-byte input data. [funcref boost::log::dump_elements `dump_elements`] accepts the same arguments, and its size-related arguments always designate the number of elements to process.
+
+ void process(std::vector< double > const& matrix)
+ {
+ // Note that dump_elements accepts the number of elements in the matrix, not its size in bytes
+ BOOST_LOG(lg) << "Matrix dump: " << logging::dump_elements(matrix.data(), matrix.size());
+ }
+
+[tip Both these manipulators can also be used with regular output streams, not necessarily loggers.]
+
+[endsect]
+
 [endsect]
 
 [section:setup Simplified library initialization tools]

Added: trunk/libs/log/src/dump.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/log/src/dump.cpp 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -0,0 +1,101 @@
+/*
+ * Copyright Andrey Semashev 2007 - 2013.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+/*!
+ * \file dump.cpp
+ * \author Andrey Semashev
+ * \date 03.05.2013
+ *
+ * \brief This header is the Boost.Log library implementation, see the library documentation
+ * at http://www.boost.org/libs/log/doc/log.html.
+ */
+
+#include <ostream>
+#include <boost/cstdint.hpp>
+#include <boost/log/utility/manipulators/dump.hpp>
+#include <boost/log/detail/header.hpp>
+
+namespace boost {
+
+BOOST_LOG_OPEN_NAMESPACE
+
+namespace aux {
+
+enum { stride = 64 };
+
+static const char g_lowercase_char_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+static const char g_uppercase_char_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+template< typename CharT >
+void dump_data(const void* data, std::size_t size, std::basic_ostream< CharT >& strm)
+{
+ typedef CharT char_type;
+
+ char_type buf[stride * 3u];
+
+ const char* const char_table = (strm.flags() & std::ios_base::uppercase) ? g_uppercase_char_table : g_lowercase_char_table;
+ const std::size_t stride_count = size / stride, tail_size = size % stride;
+
+ const uint8_t* p = static_cast< const uint8_t* >(data);
+ char_type* buf_begin = buf + 1u; // skip the first space of the first chunk
+ char_type* buf_end = buf + sizeof(buf) / sizeof(*buf);
+
+ for (std::size_t i = 0; i < stride_count; ++i)
+ {
+ char_type* b = buf;
+ for (unsigned int j = 0; j < stride; ++j, b += 3u, ++p)
+ {
+ uint32_t n = *p;
+ b[0] = static_cast< char_type >(' ');
+ b[1] = static_cast< char_type >(char_table[n >> 4]);
+ b[2] = static_cast< char_type >(char_table[n & 0x0F]);
+ }
+
+ strm.write(buf_begin, buf_end - buf_begin);
+ buf_begin = buf;
+ }
+
+ if (tail_size > 0)
+ {
+ char_type* b = buf;
+ unsigned int i = 0;
+ do
+ {
+ uint32_t n = *p;
+ b[0] = static_cast< char_type >(' ');
+ b[1] = static_cast< char_type >(char_table[n >> 4]);
+ b[2] = static_cast< char_type >(char_table[n & 0x0F]);
+ ++i;
+ ++p;
+ b += 3u;
+ }
+ while (i < tail_size);
+
+ strm.write(buf_begin, b - buf_begin);
+ }
+}
+
+template BOOST_LOG_API
+void dump_data< char >(const void* data, std::size_t size, std::basic_ostream< char >& strm);
+template BOOST_LOG_API
+void dump_data< wchar_t >(const void* data, std::size_t size, std::basic_ostream< wchar_t >& strm);
+#if !defined(BOOST_NO_CXX11_CHAR16_T)
+template BOOST_LOG_API
+void dump_data< char16_t >(const void* data, std::size_t size, std::basic_ostream< char16_t >& strm);
+#endif
+#if !defined(BOOST_NO_CXX11_CHAR32_T)
+template BOOST_LOG_API
+void dump_data< char32_t >(const void* data, std::size_t size, std::basic_ostream< char32_t >& strm);
+#endif
+
+} // namespace aux
+
+BOOST_LOG_CLOSE_NAMESPACE // namespace log
+
+} // namespace boost
+
+#include <boost/log/detail/footer.hpp>
+

Added: trunk/libs/log/test/run/util_manip_dump.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/log/test/run/util_manip_dump.cpp 2013-05-03 14:19:32 EDT (Fri, 03 May 2013)
@@ -0,0 +1,222 @@
+/*
+ * Copyright Andrey Semashev 2007 - 2013.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+/*!
+ * \file util_manip_dump.cpp
+ * \author Andrey Semashev
+ * \date 09.01.2009
+ *
+ * \brief This header contains tests for the string literals wrapper.
+ */
+
+#define BOOST_TEST_MODULE util_manip_dump
+
+#include <vector>
+#include <string>
+#include <iomanip>
+#include <sstream>
+#include <algorithm>
+#include <boost/test/unit_test.hpp>
+#include <boost/log/utility/manipulators/dump.hpp>
+#include "char_definitions.hpp"
+
+namespace logging = boost::log;
+
+// Test a short data region
+BOOST_AUTO_TEST_CASE_TEMPLATE(unbounded_binary_lowercase_short_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned char > data;
+ data.push_back(1);
+ data.push_back(2);
+ data.push_back(3);
+ data.push_back(100);
+ data.push_back(110);
+ data.push_back(120);
+ data.push_back(200);
+ data.push_back(210);
+ data.push_back(220);
+
+ ostream_type strm_dump;
+ strm_dump << logging::dump(&data[0], data.size());
+
+ ostream_type strm_correct;
+ strm_correct << "01 02 03 64 6e 78 c8 d2 dc";
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+
+// Test a short data region with uppercase formatting
+BOOST_AUTO_TEST_CASE_TEMPLATE(unbounded_binary_uppercase_short_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned char > data;
+ data.push_back(1);
+ data.push_back(2);
+ data.push_back(3);
+ data.push_back(100);
+ data.push_back(110);
+ data.push_back(120);
+ data.push_back(200);
+ data.push_back(210);
+ data.push_back(220);
+
+ ostream_type strm_dump;
+ strm_dump << std::uppercase << logging::dump(&data[0], data.size());
+
+ ostream_type strm_correct;
+ strm_correct << "01 02 03 64 6E 78 C8 D2 DC";
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+
+// Test void pointer handling
+BOOST_AUTO_TEST_CASE_TEMPLATE(unbounded_binary_pvoid_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned char > data;
+ data.push_back(1);
+ data.push_back(2);
+ data.push_back(3);
+ data.push_back(100);
+ data.push_back(110);
+ data.push_back(120);
+ data.push_back(200);
+ data.push_back(210);
+ data.push_back(220);
+
+ ostream_type strm_dump;
+ strm_dump << logging::dump((void*)&data[0], data.size());
+
+ ostream_type strm_correct;
+ strm_correct << "01 02 03 64 6e 78 c8 d2 dc";
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+
+// Test a large data region
+BOOST_AUTO_TEST_CASE_TEMPLATE(unbounded_binary_large_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned char > data;
+ ostream_type strm_correct;
+ for (unsigned int i = 0; i < 1024; ++i)
+ {
+ unsigned char n = static_cast< unsigned char >(i);
+ data.push_back(n);
+ if (i > 0)
+ strm_correct << " ";
+ strm_correct << std::hex << std::setw(2) << std::setfill(static_cast< char_type >('0')) << static_cast< unsigned int >(n);
+ }
+
+ ostream_type strm_dump;
+ strm_dump << logging::dump(&data[0], data.size());
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+
+// Test bounded dump
+BOOST_AUTO_TEST_CASE_TEMPLATE(bounded_binary_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned char > data;
+ ostream_type strm_correct;
+ for (unsigned int i = 0; i < 1024; ++i)
+ {
+ unsigned char n = static_cast< unsigned char >(i);
+ data.push_back(n);
+
+ if (i < 500)
+ {
+ if (i > 0)
+ strm_correct << " ";
+ strm_correct << std::hex << std::setw(2) << std::setfill(static_cast< char_type >('0')) << static_cast< unsigned int >(n);
+ }
+ }
+
+ strm_correct << std::dec << std::setfill(static_cast< char_type >(' ')) << " and " << 524u << " bytes more";
+
+ ostream_type strm_dump;
+ strm_dump << logging::dump(&data[0], data.size(), 500);
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+
+// Test array dump
+BOOST_AUTO_TEST_CASE_TEMPLATE(unbounded_element_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned int > data;
+ data.push_back(0x01020a0b);
+ data.push_back(0x03040c0d);
+ data.push_back(0x05060e0f);
+
+ ostream_type strm_dump;
+ strm_dump << logging::dump_elements(&data[0], data.size());
+
+ ostream_type strm_correct;
+ const unsigned char* p = reinterpret_cast< const unsigned char* >(&data[0]);
+ std::size_t size = data.size() * sizeof(unsigned int);
+ for (unsigned int i = 0; i < size; ++i)
+ {
+ unsigned char n = p[i];
+ if (i > 0)
+ strm_correct << " ";
+ strm_correct << std::hex << std::setw(2) << std::setfill(static_cast< char_type >('0')) << static_cast< unsigned int >(n);
+ }
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+
+// Test bounded array dump
+BOOST_AUTO_TEST_CASE_TEMPLATE(bounded_element_dump, CharT, char_types)
+{
+ typedef CharT char_type;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+
+ std::vector< unsigned int > data;
+ data.push_back(0x01020a0b);
+ data.push_back(0x03040c0d);
+ data.push_back(0x05060e0f);
+
+ ostream_type strm_dump;
+ strm_dump << logging::dump_elements(&data[0], data.size(), 2);
+
+ ostream_type strm_correct;
+ const unsigned char* p = reinterpret_cast< const unsigned char* >(&data[0]);
+ std::size_t size = 2 * sizeof(unsigned int);
+ for (unsigned int i = 0; i < size; ++i)
+ {
+ unsigned char n = p[i];
+ if (i > 0)
+ strm_correct << " ";
+ strm_correct << std::hex << std::setw(2) << std::setfill(static_cast< char_type >('0')) << static_cast< unsigned int >(n);
+ }
+
+ strm_correct << std::dec << std::setfill(static_cast< char_type >(' ')) << " and " << (data.size() - 2) * sizeof(unsigned int) << " bytes more";
+
+ BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str()));
+}
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk