diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/boost/libs/log | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/log')
184 files changed, 35907 insertions, 0 deletions
diff --git a/src/boost/libs/log/README.md b/src/boost/libs/log/README.md new file mode 100644 index 00000000..a91f6cd8 --- /dev/null +++ b/src/boost/libs/log/README.md @@ -0,0 +1,30 @@ +# ![Boost.Log](doc/logo.png) + +Boost.Log, part of collection of the [Boost C++ Libraries](https://github.com/boostorg), provides tools for adding logging to libraries and applications. + +### Directories + +* **build** - Boost.Log build scripts +* **config** - Boost.Log build configuration code and scripts +* **doc** - QuickBook documentation sources +* **example** - Boost.Log examples +* **include** - Interface headers of Boost.Log +* **src** - Compilable source code of Boost.Log +* **test** - Boost.Log unit tests + +### More information + +* [Documentation](https://boost.org/libs/log) +* [Ask questions](https://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-log) +* [Report bugs](https://svn.boost.org/trac/boost/newticket?component=log;version=Boost%20Release%20Branch). Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well. +* Submit your patches as pull requests against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). +* Discussions about the library are held on the [Boost developers mailing list](https://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](https://www.boost.org/community/policy.html) before posting and add the `[log]` tag at the beginning of the subject line. + +### Build status + +Master: [![AppVeyor](https://ci.appveyor.com/api/projects/status/w7x67cnm82xihei5/branch/master?svg=true)](https://ci.appveyor.com/project/Lastique/log/branch/master) [![Travis CI](https://travis-ci.org/boostorg/log.svg?branch=master)](https://travis-ci.org/boostorg/log) +Develop: [![AppVeyor](https://ci.appveyor.com/api/projects/status/w7x67cnm82xihei5/branch/develop?svg=true)](https://ci.appveyor.com/project/Lastique/log/branch/develop) [![Travis CI](https://travis-ci.org/boostorg/log.svg?branch=develop)](https://travis-ci.org/boostorg/log) + +### License + +Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). diff --git a/src/boost/libs/log/build/Jamfile.v2 b/src/boost/libs/log/build/Jamfile.v2 new file mode 100644 index 00000000..a172767a --- /dev/null +++ b/src/boost/libs/log/build/Jamfile.v2 @@ -0,0 +1,534 @@ +# +# Copyright Andrey Semashev 2007 - 2016. +# 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) +# + +import common ; +import modules ; +import os ; +import path ; +import project ; +import feature ; +import configure ; +import log-architecture ; +import log-platform-config ; +using mc ; + +local here = [ modules.binding $(__name__) ] ; + +project.push-current [ project.current ] ; +project.load [ path.join [ path.make $(here:D) ] ../config/message-compiler ] ; +project.load [ path.join [ path.make $(here:D) ] ../config/x86-ext ] ; +project.load [ path.join [ path.make $(here:D) ] ../config/pthread-mutex-robust ] ; +project.load [ path.join [ path.make $(here:D) ] ../config/native-syslog ] ; +project.load [ path.join [ path.make $(here:D) ] ../config/atomic-int32 ] ; +project.pop-current ; + +# Windows libs +lib psapi ; +lib advapi32 ; +lib secur32 ; +lib ws2_32 ; +lib mswsock ; + +# UNIX libs +lib rt ; +lib socket ; +lib nsl ; +lib ipv6 ; + +rule has-config-flag ( flag : properties * ) +{ + if ( "<define>$(flag)" in $(properties) || "<define>$(flag)=1" in $(properties) ) + { + return 1 ; + } + else + { + return ; + } +} + +rule check-instruction-set ( properties * ) +{ + local result ; + local instruction_set = [ feature.get-values "log-instruction-set" : [ log-architecture.deduce-instruction-set $(properties) ] ] ; + + if $(instruction_set) = i386 || $(instruction_set) = i486 + { + if ! $(.annouced-failure) + { + ECHO Boost.Log is not supported on the specified target CPU and will not be built. At least i586 class CPU is required. ; + .annouced-failure = 1 ; + } + result = <build>no ; + } + + return $(result) ; +} + +rule select-regex-backend ( properties * ) +{ + local result = ; + + # Use Boost.Regex backend by default. It produces smaller executables and also has the best performance for small string matching. + if ! ( + [ has-config-flag BOOST_LOG_WITHOUT_SETTINGS_PARSERS : $(properties) ] || + [ has-config-flag BOOST_LOG_WITHOUT_DEFAULT_FACTORIES : $(properties) ] || + [ has-config-flag BOOST_LOG_USE_STD_REGEX : $(properties) ] || + [ has-config-flag BOOST_LOG_USE_BOOST_XPRESSIVE : $(properties) ] ) + { + result = <library>/boost/regex//boost_regex ; + } + + return $(result) ; +} + +rule check-pthread-mutex-robust ( properties * ) +{ + local result = ; + + local has_pthread_mutex_robust = [ configure.builds /boost/log/pthread-mutex-robust//pthread_mutex_robust : $(properties) : pthread-supports-robust-mutexes ] ; + if $(has_pthread_mutex_robust) + { + result = <define>BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST ; + } + + return $(result) ; +} + +rule check-atomic-int32 ( properties * ) +{ + local result = ; + + local has_atomic_int32 = [ configure.builds /boost/log/atomic-int32//atomic_int32 : $(properties) : native-atomic-int32-supported ] ; + if ! $(has_atomic_int32) + { + result = <define>BOOST_LOG_WITHOUT_IPC ; + } + + return $(result) ; +} + +rule check-native-syslog ( properties * ) +{ + local result = ; + + if ! [ has-config-flag BOOST_LOG_WITHOUT_SYSLOG : $(properties) ] + { + local has_native_syslog = [ configure.builds /boost/log/native-syslog//native_syslog : $(properties) : native-syslog-supported ] ; + if $(has_native_syslog) + { + result = <define>BOOST_LOG_USE_NATIVE_SYSLOG ; + } + } + + return $(result) ; +} + +rule check-message-compiler ( properties * ) +{ + local result ; + + if <target-os>windows in $(properties) + { + if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] + { + local has_mc = [ configure.builds /boost/log/message-compiler//test-availability : $(properties) : message-compiler ] ; + if ! $(has_mc) + { + result = <define>BOOST_LOG_WITHOUT_EVENT_LOG ; + } + } + else + { + # This branch is needed to fix building with MinGW + result = <define>BOOST_LOG_WITHOUT_EVENT_LOG ; + } + } + else + { + result = <define>BOOST_LOG_WITHOUT_EVENT_LOG ; + } + + return $(result) ; +} + +project boost/log + : source-location ../src + : requirements + <conditional>@check-instruction-set + <conditional>@check-atomic-int32 + <conditional>@select-regex-backend + <conditional>@check-pthread-mutex-robust + <conditional>@check-native-syslog + <conditional>@check-message-compiler + <conditional>@log-platform-config.set-platform-defines + + <include>../src + + <define>__STDC_CONSTANT_MACROS # Use system-defined macros for integer constants, if possible + <define>BOOST_SPIRIT_USE_PHOENIX_V3=1 + <define>BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono + + # Disable warnings about using 'insecure' standard C functions + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>clang:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + <toolset>gcc,<target-os>windows:<linkflags>-Wl,--enable-auto-import + <toolset>gcc,<target-os>cygwin:<linkflags>-Wl,--enable-auto-import + + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/atomic//boost_atomic + <threading>multi:<library>/boost/thread//boost_thread + + <target-os>windows:<library>ws2_32 + <target-os>windows:<library>mswsock + <target-os>windows:<library>advapi32 + + <target-os>cygwin:<library>ws2_32 + <target-os>cygwin:<library>mswsock + <target-os>cygwin:<library>advapi32 + # Boost.Interprocess does not compile on Cygwin: https://github.com/boostorg/interprocess/issues/76 + <target-os>cygwin:<define>BOOST_LOG_WITHOUT_IPC + + <target-os>linux:<library>rt + + <target-os>solaris:<library>socket + <target-os>solaris:<library>nsl + + <target-os>hpux:<library>ipv6 + + <target-os>freebsd:<library>rt + <target-os>qnxnto:<library>socket + <toolset>pgi:<library>rt + : usage-requirements + <toolset>clang:<cxxflags>-Wno-bind-to-temporary-copy + <toolset>clang:<cxxflags>-Wno-unused-function + ; + +local BOOST_LOG_COMMON_SRC = + attribute_name.cpp + attribute_set.cpp + attribute_value_set.cpp + code_conversion.cpp + core.cpp + record_ostream.cpp + severity_level.cpp + global_logger_storage.cpp + named_scope.cpp + process_name.cpp + process_id.cpp + thread_id.cpp + timer.cpp + exceptions.cpp + default_attribute_names.cpp + default_sink.cpp + text_ostream_backend.cpp + text_file_backend.cpp + text_multifile_backend.cpp + thread_specific.cpp + once_block.cpp + timestamp.cpp + threadsafe_queue.cpp + event.cpp + trivial.cpp + spirit_encoding.cpp + format_parser.cpp + date_time_format_parser.cpp + named_scope_format_parser.cpp + permissions.cpp + dump.cpp + ; + +BOOST_LOG_COMMON_SSSE3_SRC = + dump_ssse3 + ; + +BOOST_LOG_COMMON_AVX2_SRC = + dump_avx2 + ; + +rule ssse3-targets-cond ( properties * ) +{ + local result = <build>no ; + + if <log-architecture>x86 in [ log-architecture.deduce-architecture $(properties) ] + { + local has_ssse3 = [ configure.builds /boost/log/x86-extensions//ssse3 : $(properties) : compiler-supports-ssse3 ] ; + if $(has_ssse3) + { + result = ; + if <toolset>gcc in $(properties) || <toolset>clang in $(properties) + { + result = <cxxflags>"-msse -msse2 -msse3 -mssse3" ; + } + else if <toolset>intel in $(properties) + { + if <toolset-intel:platform>win in $(properties) + { + result = <cxxflags>"/QxSSSE3" ; + } + else + { + result = <cxxflags>"-xSSSE3" ; + } + } + else if <toolset>msvc in $(properties) + { + # MSVC doesn't really care about these switches, all SSE intrinsics are always available, but still... + # Also 64 bit MSVC doesn't have the /arch:SSE2 switch as it is the default. + if <log-address-model>32 in [ log-architecture.deduce-address-model $(properties) ] + { + result = <cxxflags>"/arch:SSE2" ; + } + } + } + } + +# if ! <build>no in $(result) +# { +# ECHO Boost.Log: Using SSSE3 optimized implementation ; +# } +# ECHO $(result) ; + + return $(result) ; +} + +for local src in $(BOOST_LOG_COMMON_SSSE3_SRC) +{ + obj $(src) + : ## sources ## + $(src).cpp + : ## requirements ## + <conditional>@ssse3-targets-cond + <link>shared:<define>BOOST_LOG_DLL + <define>BOOST_LOG_BUILDING_THE_LIB=1 + : ## default-build ## + : ## usage-requirements ## + <define>BOOST_LOG_USE_SSSE3 + ; + + explicit $(src) ; +} + +rule avx2-targets-cond ( properties * ) +{ + local result = <build>no ; + + if <log-architecture>x86 in [ log-architecture.deduce-architecture $(properties) ] + { + local has_avx2 = [ configure.builds /boost/log/x86-extensions//avx2 : $(properties) : compiler-supports-avx2 ] ; + if $(has_avx2) + { + result = ; + if <toolset>gcc in $(properties) + { + result = <cxxflags>"-mavx -mavx2 -fabi-version=0" ; + } + else if <toolset>clang in $(properties) + { + result = <cxxflags>"-mavx -mavx2" ; + } + else if <toolset>intel in $(properties) + { + if <toolset-intel:platform>win in $(properties) + { + result = <cxxflags>"/arch:CORE-AVX2" ; + } + else + { + result = <cxxflags>"-xCORE-AVX2 -fabi-version=0" ; + } + } + else if <toolset>msvc in $(properties) + { + result = <cxxflags>"/arch:AVX" ; + } + } + } + +# if ! <build>no in $(result) +# { +# ECHO Boost.Log: Using AVX2 optimized implementation ; +# } +# ECHO $(result) ; + + return $(result) ; +} + +for local src in $(BOOST_LOG_COMMON_AVX2_SRC) +{ + obj $(src) + : ## sources ## + $(src).cpp + : ## requirements ## + <conditional>@avx2-targets-cond + <link>shared:<define>BOOST_LOG_DLL + <define>BOOST_LOG_BUILDING_THE_LIB=1 + : ## default-build ## + : ## usage-requirements ## + <define>BOOST_LOG_USE_AVX2 + ; + + explicit $(src) ; +} + +rule select-arch-specific-sources ( properties * ) +{ + local result ; + + if <log-architecture>x86 in [ log-architecture.deduce-architecture $(properties) ] + { + local has_ssse3 = [ configure.builds /boost/log/x86-extensions//ssse3 : $(properties) : compiler-supports-ssse3 ] ; + if $(has_ssse3) + { + result += <define>BOOST_LOG_USE_SSSE3 ; + result += <source>$(BOOST_LOG_COMMON_SSSE3_SRC) ; + } + + local has_avx2 = [ configure.builds /boost/log/x86-extensions//avx2 : $(properties) : compiler-supports-avx2 ] ; + if $(has_avx2) + { + result += <define>BOOST_LOG_USE_AVX2 ; + result += <source>$(BOOST_LOG_COMMON_AVX2_SRC) ; + } + } + +# ECHO Arch sources: $(result) ; + + return $(result) ; +} + +rule select-platform-specific-sources ( properties * ) +{ + local result ; + + if <target-os>windows in $(properties) + { + result += <source>windows/light_rw_mutex.cpp ; + + if ! [ has-config-flag BOOST_LOG_WITHOUT_IPC : $(properties) ] + { + result += <source>windows/object_name.cpp ; + result += <source>windows/mapped_shared_memory.cpp ; + result += <source>windows/ipc_sync_wrappers.cpp ; + result += <source>windows/ipc_reliable_message_queue.cpp ; + result += <library>secur32 ; + } + + if ! [ has-config-flag BOOST_LOG_WITHOUT_DEBUG_OUTPUT : $(properties) ] + { + result += <source>windows/debug_output_backend.cpp ; + } + + if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] + { + result += <source>windows/simple_event_log.mc ; + result += <source>windows/event_log_backend.cpp ; + result += <library>psapi ; + + DEPENDS windows/event_log_backend.cpp : windows/simple_event_log.mc ; + } + } + else + { + result += <define>BOOST_LOG_WITHOUT_EVENT_LOG ; + result += <define>BOOST_LOG_WITHOUT_DEBUG_OUTPUT ; + + if ! [ has-config-flag BOOST_LOG_WITHOUT_IPC : $(properties) ] + { + result += <source>posix/object_name.cpp ; + result += <source>posix/ipc_reliable_message_queue.cpp ; + } + } + + if ! [ has-config-flag BOOST_LOG_WITHOUT_SYSLOG : $(properties) ] + { + result += <source>syslog_backend.cpp ; + } + +# ECHO Platform sources: $(result) ; + + return $(result) ; +} + +lib boost_log + : ## sources ## + $(BOOST_LOG_COMMON_SRC) + : ## requirements ## + <conditional>@select-arch-specific-sources + <conditional>@select-platform-specific-sources + <link>shared:<define>BOOST_LOG_DLL + <define>BOOST_LOG_BUILDING_THE_LIB=1 + : ## default-build ## + : ## usage-requirements ## + <link>shared:<define>BOOST_LOG_DYN_LINK=1 + <threading>single:<define>BOOST_LOG_NO_THREADS + ; + + +local BOOST_LOG_SETUP_COMMON_SRC = + parser_utils.cpp + init_from_stream.cpp + init_from_settings.cpp + settings_parser.cpp + filter_parser.cpp + formatter_parser.cpp + default_filter_factory.cpp + matches_relation_factory.cpp + default_formatter_factory.cpp + ; + +lib boost_log_setup + : ## sources ## + setup/$(BOOST_LOG_SETUP_COMMON_SRC) + : ## requirements ## + <link>shared:<define>BOOST_LOG_DYN_LINK=1 + <link>shared:<define>BOOST_LOG_SETUP_DLL + <define>BOOST_LOG_SETUP_BUILDING_THE_LIB=1 + <library>boost_log + : ## default-build ## + : ## usage-requirements ## + <link>shared:<define>BOOST_LOG_SETUP_DYN_LINK=1 + <threading>single:<define>BOOST_LOG_NO_THREADS + ; + +boost-install boost_log boost_log_setup ; diff --git a/src/boost/libs/log/build/log-architecture.jam b/src/boost/libs/log/build/log-architecture.jam new file mode 100644 index 00000000..785dda0f --- /dev/null +++ b/src/boost/libs/log/build/log-architecture.jam @@ -0,0 +1,112 @@ +# log-architecture.jam +# +# Copyright 2012 Steven Watanabe +# Copyright 2013 Andrey Semashev +# +# 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) + +import configure ; +import project ; +import path ; +import property ; +import feature ; + +local here = [ modules.binding $(__name__) ] ; + +feature.feature log-architecture : : free ; +feature.feature log-address-model : : free ; +feature.feature log-instruction-set : : free ; + +project.push-current [ project.current ] ; +project.load [ path.join [ path.make $(here:D) ] ../../config/checks/architecture ] ; +project.pop-current ; + +rule deduce-address-model ( properties * ) +{ + local address_model = [ feature.get-values "address-model" : $(properties) ] ; + if $(address_model) + { + return <log-address-model>$(address_model) ; + } + else + { + if [ configure.builds /boost/architecture//32 : $(properties) : 32-bit ] + { + return <log-address-model>32 ; + } + else if [ configure.builds /boost/architecture//64 : $(properties) : 64-bit ] + { + return <log-address-model>64 ; + } + } +} + +rule address-model ( ) +{ + return <conditional>@log-architecture.deduce-address-model ; +} + +rule deduce-architecture ( properties * ) +{ + local architecture = [ feature.get-values "architecture" : $(properties) ] ; + if $(architecture) + { + return <log-architecture>$(architecture) ; + } + else + { + if [ configure.builds /boost/architecture//x86 : $(properties) : x86 ] + { + return <log-architecture>x86 ; + } + else if [ configure.builds /boost/architecture//arm : $(properties) : arm ] + { + return <log-architecture>arm ; + } + else if [ configure.builds /boost/architecture//mips1 : $(properties) : mips1 ] + { + return <log-architecture>mips1 ; + } + else if [ configure.builds /boost/architecture//power : $(properties) : power ] + { + return <log-architecture>power ; + } + else if [ configure.builds /boost/architecture//sparc : $(properties) : sparc ] + { + return <log-architecture>sparc ; + } + } +} + +rule architecture ( ) +{ + return <conditional>@log-architecture.deduce-architecture ; +} + +rule deduce-instruction-set ( properties * ) +{ + local result ; + local instruction_set = [ feature.get-values "instruction-set" : $(properties) ] ; + + if $(instruction_set) + { + result = <log-instruction-set>$(instruction_set) ; + } + else + { + if <log-architecture>x86 in $(properties) && <log-address-model>32 in $(properties) + { + # We build for Pentium Pro and later CPUs by default. This is used as the target in many Linux distributions, and Windows and OS X also seem to not support older CPUs. + result = <log-instruction-set>i686 ; + } + } + + return $(result) ; +} + +rule instruction-set ( ) +{ + return <conditional>@log-architecture.deduce-instruction-set ; +} diff --git a/src/boost/libs/log/build/log-platform-config.jam b/src/boost/libs/log/build/log-platform-config.jam new file mode 100644 index 00000000..cee01cc1 --- /dev/null +++ b/src/boost/libs/log/build/log-platform-config.jam @@ -0,0 +1,59 @@ +# log-platform-config.jam +# +# Copyright 2017 Andrey Semashev +# +# 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) + +import configure ; +import project ; +import path ; +import property ; +import feature ; + +local here = [ modules.binding $(__name__) ] ; + +project.push-current [ project.current ] ; +project.load [ path.join [ path.make $(here:D) ] ../config/xopen-source-600 ] ; +project.pop-current ; + +rule set-platform-defines ( properties * ) +{ + local result = ; + + if ( <target-os>windows in $(properties) ) || ( <target-os>cygwin in $(properties) ) + { + result += <define>NOMINMAX ; + result += <define>WIN32_LEAN_AND_MEAN ; + result += <define>SECURITY_WIN32 ; + result += <define>BOOST_USE_WINDOWS_H ; + + if <target-os>cygwin in $(properties) + { + result += <define>__USE_W32_SOCKETS ; + result += <define>_XOPEN_SOURCE=600 ; + } + } + else if <target-os>solaris in $(properties) + { + # Solaris headers are broken and cannot be included in C++03 when _XOPEN_SOURCE=600. At the same time, they cannot be included with _XOPEN_SOURCE=500 in C++11 and later. + # This is because the system headers check the C language version and error out if the version does not match. We have to test if we can request _XOPEN_SOURCE=600. + if [ configure.builds /boost/log/xopen-source-600//xopen_source_600 : $(properties) : xopen-source-600-supported ] + { + result += <define>_XOPEN_SOURCE=600 ; + } + else + { + result += <define>_XOPEN_SOURCE=500 ; + } + + result += <define>__EXTENSIONS__ ; + } + else if ( <target-os>linux in $(properties) ) || ( <target-os>hpux in $(properties) ) + { + result += <define>_XOPEN_SOURCE=600 ; + } + + return $(result) ; +} diff --git a/src/boost/libs/log/config/atomic-int32/Jamfile.jam b/src/boost/libs/log/config/atomic-int32/Jamfile.jam new file mode 100644 index 00000000..5b08d8d7 --- /dev/null +++ b/src/boost/libs/log/config/atomic-int32/Jamfile.jam @@ -0,0 +1,19 @@ +# +# Copyright Andrey Semashev 2016. +# 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) +# + +import project ; +import log-platform-config ; + +project /boost/log/atomic-int32 + : source-location . + : requirements + <conditional>@log-platform-config.set-platform-defines + + <pch>off + ; + +obj atomic_int32 : atomic_int32.cpp ; diff --git a/src/boost/libs/log/config/atomic-int32/atomic_int32.cpp b/src/boost/libs/log/config/atomic-int32/atomic_int32.cpp new file mode 100644 index 00000000..f39bcc8f --- /dev/null +++ b/src/boost/libs/log/config/atomic-int32/atomic_int32.cpp @@ -0,0 +1,17 @@ +/* + * Copyright Andrey Semashev 2016. + * 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) + */ + +#include <boost/atomic.hpp> + +#if !defined(BOOST_ATOMIC_INT32_LOCK_FREE) || (BOOST_ATOMIC_INT32_LOCK_FREE+0) != 2 +#error Boost.Log: Native 32-bit atomic operations are required but not supported by Boost.Atomic on the target platform +#endif + +int main(int, char*[]) +{ + return 0; +} diff --git a/src/boost/libs/log/config/message-compiler/Jamfile.jam b/src/boost/libs/log/config/message-compiler/Jamfile.jam new file mode 100644 index 00000000..37d98d10 --- /dev/null +++ b/src/boost/libs/log/config/message-compiler/Jamfile.jam @@ -0,0 +1,22 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import project ; +import log-platform-config ; +using mc ; + +project /boost/log/message-compiler + : source-location ../../src + : requirements + <conditional>@log-platform-config.set-platform-defines + <pch>off + ; + +obj simple_event_log : windows/simple_event_log.mc ; + +# This test target verifies that Message Compiler (mc) is available and supported by the current toolset +alias test-availability : simple_event_log ; diff --git a/src/boost/libs/log/config/native-syslog/Jamfile.jam b/src/boost/libs/log/config/native-syslog/Jamfile.jam new file mode 100644 index 00000000..fa5dfcd0 --- /dev/null +++ b/src/boost/libs/log/config/native-syslog/Jamfile.jam @@ -0,0 +1,19 @@ +# +# Copyright Andrey Semashev 2016. +# 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) +# + +import project ; +import log-platform-config ; + +project /boost/log/native-syslog + : source-location . + : requirements + <conditional>@log-platform-config.set-platform-defines + + <pch>off + ; + +obj native_syslog : native_syslog.cpp ; diff --git a/src/boost/libs/log/config/native-syslog/native_syslog.cpp b/src/boost/libs/log/config/native-syslog/native_syslog.cpp new file mode 100644 index 00000000..25605a68 --- /dev/null +++ b/src/boost/libs/log/config/native-syslog/native_syslog.cpp @@ -0,0 +1,26 @@ +/* + * Copyright Andrey Semashev 2016. + * 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) + */ + +#include <syslog.h> + +int main(int, char*[]) +{ + ::openlog("test", LOG_NDELAY, LOG_USER); + + ::syslog(LOG_USER | LOG_DEBUG, "debug message"); + ::syslog(LOG_USER | LOG_INFO, "info message"); + ::syslog(LOG_USER | LOG_NOTICE, "notice message"); + ::syslog(LOG_USER | LOG_WARNING, "warning message"); + ::syslog(LOG_USER | LOG_ERR, "error message"); + ::syslog(LOG_USER | LOG_CRIT, "critical message"); + ::syslog(LOG_USER | LOG_ALERT, "alert message"); + ::syslog(LOG_USER | LOG_EMERG, "emergency message"); + + ::closelog(); + + return 0; +} diff --git a/src/boost/libs/log/config/pthread-mutex-robust/Jamfile.jam b/src/boost/libs/log/config/pthread-mutex-robust/Jamfile.jam new file mode 100644 index 00000000..6082e66e --- /dev/null +++ b/src/boost/libs/log/config/pthread-mutex-robust/Jamfile.jam @@ -0,0 +1,19 @@ +# +# Copyright Andrey Semashev 2015. +# 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) +# + +import project ; +import log-platform-config ; + +project /boost/log/pthread-mutex-robust + : source-location . + : requirements + <conditional>@log-platform-config.set-platform-defines + + <pch>off + ; + +obj pthread_mutex_robust : pthread_mutex_robust.cpp ; diff --git a/src/boost/libs/log/config/pthread-mutex-robust/pthread_mutex_robust.cpp b/src/boost/libs/log/config/pthread-mutex-robust/pthread_mutex_robust.cpp new file mode 100644 index 00000000..36415aca --- /dev/null +++ b/src/boost/libs/log/config/pthread-mutex-robust/pthread_mutex_robust.cpp @@ -0,0 +1,37 @@ +/* + * Copyright Andrey Semashev 2015. + * 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) + */ + +#include <errno.h> +#include <pthread.h> + +int main(int, char*[]) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); + + pthread_mutex_t m; + pthread_mutex_init(&m, &attr); + pthread_mutexattr_destroy(&attr); + + int err = pthread_mutex_lock(&m); + if (err == EOWNERDEAD) + { + err = pthread_mutex_consistent(&m); + } + + if (err != ENOTRECOVERABLE) + { + pthread_mutex_unlock(&m); + } + + pthread_mutex_destroy(&m); + + return 0; +} diff --git a/src/boost/libs/log/config/x86-ext/Jamfile.jam b/src/boost/libs/log/config/x86-ext/Jamfile.jam new file mode 100644 index 00000000..0e9695aa --- /dev/null +++ b/src/boost/libs/log/config/x86-ext/Jamfile.jam @@ -0,0 +1,34 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import project ; + +project /boost/log/x86-extensions + : source-location . + : requirements + <pch>off + ; + +obj ssse3 : ssse3.cpp + : + <toolset>gcc:<cxxflags>"-msse -msse2 -msse3 -mssse3" + <toolset>clang:<cxxflags>"-msse -msse2 -msse3 -mssse3" + <toolset>intel-linux:<cxxflags>"-xSSSE3" + <toolset>intel-darwin:<cxxflags>"-xSSSE3" + <toolset>intel-win:<cxxflags>"/QxSSSE3" + ; + +obj avx2 : avx2.cpp + : + <toolset>gcc:<cxxflags>"-mavx -mavx2 -fabi-version=0" + <toolset>clang:<cxxflags>"-mavx -mavx2" + <toolset>intel-linux:<cxxflags>"-xCORE-AVX2 -fabi-version=0" + <toolset>intel-darwin:<cxxflags>"-xCORE-AVX2 -fabi-version=0" + <toolset>intel-win:<cxxflags>"/arch:CORE-AVX2" + <toolset>msvc:<cxxflags>"/arch:AVX" + ; + diff --git a/src/boost/libs/log/config/x86-ext/avx2.cpp b/src/boost/libs/log/config/x86-ext/avx2.cpp new file mode 100644 index 00000000..3ee051f1 --- /dev/null +++ b/src/boost/libs/log/config/x86-ext/avx2.cpp @@ -0,0 +1,20 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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) + */ + +#include <immintrin.h> + +void pretend_used(__m256i*); + +int main(int, char*[]) +{ + __m256i mm = _mm256_setzero_si256(); + pretend_used(&mm); + mm = _mm256_shuffle_epi8(_mm256_alignr_epi8(mm, mm, 10), mm); + pretend_used(&mm); + _mm256_zeroupper(); + return 0; +} diff --git a/src/boost/libs/log/config/x86-ext/ssse3.cpp b/src/boost/libs/log/config/x86-ext/ssse3.cpp new file mode 100644 index 00000000..4bcfe2bc --- /dev/null +++ b/src/boost/libs/log/config/x86-ext/ssse3.cpp @@ -0,0 +1,19 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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) + */ + +#include <tmmintrin.h> + +void pretend_used(__m128i*); + +int main(int, char*[]) +{ + __m128i mm = _mm_setzero_si128(); + pretend_used(&mm); + mm = _mm_shuffle_epi8(_mm_alignr_epi8(mm, mm, 10), mm); + pretend_used(&mm); + return 0; +} diff --git a/src/boost/libs/log/config/xopen-source-600/Jamfile.jam b/src/boost/libs/log/config/xopen-source-600/Jamfile.jam new file mode 100644 index 00000000..0d744dcf --- /dev/null +++ b/src/boost/libs/log/config/xopen-source-600/Jamfile.jam @@ -0,0 +1,16 @@ +# +# Copyright Andrey Semashev 2017. +# 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) +# + +import project ; + +project /boost/log/xopen-source-600 + : source-location . + : requirements + <pch>off + ; + +obj xopen_source_600 : xopen_source_600.cpp ; diff --git a/src/boost/libs/log/config/xopen-source-600/xopen_source_600.cpp b/src/boost/libs/log/config/xopen-source-600/xopen_source_600.cpp new file mode 100644 index 00000000..1b26467c --- /dev/null +++ b/src/boost/libs/log/config/xopen-source-600/xopen_source_600.cpp @@ -0,0 +1,17 @@ +/* + * Copyright Andrey Semashev 2017. + * 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) + */ + +// Test if we can include system headers with -D_XOPEN_SOURCE=600 +#undef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 + +#include <unistd.h> + +int main(int, char*[]) +{ + return 0; +} diff --git a/src/boost/libs/log/example/Jamfile.v2 b/src/boost/libs/log/example/Jamfile.v2 new file mode 100644 index 00000000..07ad6ccf --- /dev/null +++ b/src/boost/libs/log/example/Jamfile.v2 @@ -0,0 +1,23 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +build-project ./advanced_usage ; +build-project ./async_log ; +build-project ./bounded_async_log ; +build-project ./basic_usage ; +build-project ./event_log ; +build-project ./multiple_files ; +build-project ./multiple_threads ; +build-project ./rotating_file ; +build-project ./settings_file ; +build-project ./settings_file_custom_factories ; +build-project ./syslog ; +build-project ./native_syslog ; +build-project ./wide_char ; +build-project ./keywords ; +build-project ./trivial ; +build-project ./doc ; diff --git a/src/boost/libs/log/example/advanced_usage/Jamfile.v2 b/src/boost/libs/log/example/advanced_usage/Jamfile.v2 new file mode 100644 index 00000000..542bd18b --- /dev/null +++ b/src/boost/libs/log/example/advanced_usage/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe advanced_usage + : main.cpp + ; diff --git a/src/boost/libs/log/example/advanced_usage/main.cpp b/src/boost/libs/log/example/advanced_usage/main.cpp new file mode 100644 index 00000000..dcfd1ee8 --- /dev/null +++ b/src/boost/libs/log/example/advanced_usage/main.cpp @@ -0,0 +1,300 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 11.11.2007 + * + * \brief An example of in-depth library usage. See the library tutorial for expanded + * comments on this code. It may also be worthwhile reading the Wiki requirements page: + * http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging + */ + +// #define BOOST_LOG_USE_CHAR +// #define BOOST_ALL_DYN_LINK 1 +// #define BOOST_LOG_DYN_LINK 1 + +#include <cassert> +#include <iostream> +#include <fstream> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/core/null_deleter.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks.hpp> +#include <boost/log/sources/logger.hpp> +#include <boost/log/utility/manipulators/add_value.hpp> +#include <boost/log/attributes/scoped_attribute.hpp> +#include <boost/log/support/date_time.hpp> + +namespace logging = boost::log; +namespace expr = boost::log::expressions; +namespace sinks = boost::log::sinks; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +// Here we define our application severity levels. +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +// The formatting logic for the severity level +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl) +{ + static const char* const str[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str))) + strm << str[lvl]; + else + strm << static_cast< int >(lvl); + return strm; +} + +int foo(src::logger& lg) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG(lg) << "foo is being called"; + return 10; +} + +int main(int argc, char* argv[]) +{ + // This is a in-depth tutorial/example of Boost.Log usage + + // The first thing we have to do to get using the library is + // to set up the logging sinks - i.e. where the logs will be written to. + // Each sink is composed from frontend and backend. Frontend deals with + // general sink behavior, like filtering (see below) and threading model. + // Backend implements formatting and, actually, storing log records. + // Not every frontend/backend combinations are compatible (mostly because of + // threading models incompatibilities), but if they are not, the code will + // simply not compile. + + // For now we only create a text output sink: + typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink; + shared_ptr< text_sink > pSink(new text_sink); + + // Here synchronous_sink is a sink frontend that performs thread synchronization + // before passing log records to the backend (the text_ostream_backend class). + // The backend formats each record and outputs it to one or several streams. + // This approach makes implementing backends a lot simpler, because you don't + // need to worry about multithreading. + + { + // The good thing about sink frontends is that they are provided out-of-box and + // take away thread-safety burden from the sink backend implementors. Even if you + // have to call a custom backend method, the frontend gives you a convenient way + // to do it in a thread safe manner. All you need is to acquire a locking pointer + // to the backend. + text_sink::locked_backend_ptr pBackend = pSink->locked_backend(); + + // Now, as long as pBackend lives, you may work with the backend without + // interference of other threads that might be trying to log. + + // Next we add streams to which logging records should be output + shared_ptr< std::ostream > pStream(&std::clog, boost::null_deleter()); + pBackend->add_stream(pStream); + + // We can add more than one stream to the sink backend + shared_ptr< std::ofstream > pStream2(new std::ofstream("sample.log")); + assert(pStream2->is_open()); + pBackend->add_stream(pStream2); + } + + // Ok, we're ready to add the sink to the logging library + logging::core::get()->add_sink(pSink); + + // Now our logs will be written both to the console and to the file. + // Let's do a quick test and output something. We have to create a logger for this. + src::logger lg; + + // And output... + BOOST_LOG(lg) << "Hello, World!"; + + // Nice, huh? That's pretty much equivalent to writing the string to both the file + // and the console. Now let's define the different way of formatting log records. + // Each logging record may have a number of attributes in addition to the + // message body itself. By setting up formatter we define which of them + // will be written to log and in what way they will look there. + pSink->set_formatter(expr::stream + << expr::attr< unsigned int >("RecordID") // First an attribute "RecordID" is written to the log + << " [" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f") + << "] [" << expr::attr< severity_level >("Severity") + << "] [" << expr::attr< boost::posix_time::time_duration >("Uptime") + << "] [" // then this delimiter separates it from the rest of the line + << expr::if_(expr::has_attr("Tag")) + [ + expr::stream << expr::attr< std::string >("Tag") // then goes another attribute named "Tag" + // Note here we explicitly stated that its type + // should be std::string. We could omit it just + // like we did it with the "RecordID", but in this case + // library would have to detect the actual attribute value + // type in run time which has the following consequences: + // - On the one hand, the attribute would have been output + // even if it has another type (not std::string). + // - On the other, this detection does not come for free + // and will result in performance decrease. + // + // In general it's better you to specify explicitly which + // type should an attribute have wherever it is possible. + // You may specify an MPL sequence of types if the attribute + // may have more than one type. And you will have to specify + // it anyway if the library is not familiar with it (see + // boost/log/utility/type_dispatch/standard_types.hpp for the list + // of the supported out-of-the-box types). + << "] [" // yet another delimiter + ] + << expr::format_named_scope("Scope", keywords::format = "%n", keywords::iteration = expr::reverse) << "] " + << expr::smessage); // here goes the log record text + +/* + // There is an alternative way of specifying formatters + pSink->set_formatter( + expr::format("%1% @ %2% [%3%] >%4%< Scope: %5%: %6%") + % expr::attr< unsigned int >("RecordID") + % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f") + % expr::attr< boost::posix_time::time_duration >("Uptime") + % expr::attr< std::string >("Tag") + % expr::format_named_scope("Scope", keywords::format = "%n", keywords::iteration = expr::reverse, keywords::depth = 2) + % expr::smessage); +*/ + + // Now the sink will output in the following format: + // 1 [Current time] [Tag value] Hello World! + // The output will be the same for all streams we add to the sink. If you want something different, + // you may create another sink for that purpose. + + // Now we're going to set up the attributes. + // Remember that "RecordID" attribute in the formatter? There is a counter + // attribute in the library that increments or decrements the value each time + // it is output. Let's create it with a starting value 1. + attrs::counter< unsigned int > RecordID(1); + + // Since we intend to count all logging records ever made by the application, + // this attribute should clearly be global. + logging::core::get()->add_global_attribute("RecordID", RecordID); + + // And similarly add a time stamp + attrs::local_clock TimeStamp; + logging::core::get()->add_global_attribute("TimeStamp", TimeStamp); + + // And an up time stopwatch + BOOST_LOG_SCOPED_THREAD_ATTR("Uptime", attrs::timer()); + + // Attributes may have two other scopes: thread scope and source scope. Attributes of thread + // scope are output with each record made by the thread (regardless of the logger object), and + // attributes of the source scope are output with each record made by the logger. On output + // all attributes of global, thread and source scopes are merged into a one record and passed to + // the sinks as one view. There is no difference between attributes of different scopes from the + // sinks' perspective. + + // Let's also track the execution scope from which the records are made + attrs::named_scope Scope; + logging::core::get()->add_thread_attribute("Scope", Scope); + + // We can mark the current execution scope now - it's the 'main' function + BOOST_LOG_FUNCTION(); + + // Let's try out the counter attribute and formatting + BOOST_LOG(lg) << "Some log line with a counter"; + BOOST_LOG(lg) << "Another log line with the counter"; + + // Ok, remember the "Tag" attribute we added in the formatter? It is absent in these + // two lines above, so it is empty in the output. Let's try to tag some log records with it. + { + BOOST_LOG_NAMED_SCOPE("Tagging scope"); + + // Here we add a temporary attribute to the logger lg. + // Every log record being written in the current scope with logger lg + // will have a string attribute "Tag" with value "Tagged line" attached. + BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Tag", "Tagged line"); + + // The above line is roughly equivalent to the following: + // attrs::constant< std::string > TagAttr("Tagged line"); + // logging::scoped_attribute _ = + // logging::add_scoped_logger_attribute(lg, "Tag", TagAttr); + + // Now these lines will be highlighted with the tag + BOOST_LOG(lg) << "Some tagged log line"; + BOOST_LOG(lg) << "Another tagged log line"; + } + + // And this line is not highlighted anymore + BOOST_LOG(lg) << "Now the tag is removed"; + BOOST_LOG(lg) << logging::add_value("Tag", "Tagged line") << "Some lines can also be selectively tagged"; + + // Now let's try to apply filtering to the output. Filtering is based on + // attributes being output with the record. One of the common filtering use cases + // is filtering based on the record severity level. We've already defined severity levels. + // Now we can set the filter. A filter is essentially a functor that returns + // boolean value that tells whether to write the record or not. + pSink->set_filter( + expr::attr< severity_level >("Severity").or_default(normal) >= warning // Write all records with "warning" severity or higher + || expr::begins_with(expr::attr< std::string >("Tag").or_default(std::string()), "IMPORTANT")); // ...or specifically tagged + + // The "attr" placeholder here acts pretty much like the "attr" placeholder in formatters, except + // that it requires the attribute type (or types in MPL-sequence) to be specified. + // In case of a single std::string or std::wstring type of attribute the "attr" placeholder + // provides a number of extended predicates which include "begins_with", "ends_with", "contains" + // and "matches" (the last one performs RegEx matching). + // There are other placeholders to be used for filter composition in the "boost/log/filters" + // directory. Additionally, you are not restricted to them and may provide your own filtering + // functors. + + // It must be noted that filters may be applied on per-sink basis and/or globally. + // Above we set a filter for this particular sink. Had we another sink, the filter would + // not influence it. To set a global filter one should call the set_filter method of the + // logging system like that: + // logging::core::get()->set_filter(...); + + // Now, to set logging severity we could perfectly use our previously created logger "lg". + // But no make it more convenient and efficient there is a special extended logger class. + // Its implementation may serve as an example of extending basic library functionality. + // You may add your specific capabilities to the logger by deriving your class from it. + src::severity_logger< severity_level > slg; + + // These two lines test filtering based on severity + BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the output"; + BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the output"; + + { + // Next we try if the second condition of the filter works + // We mark following lines with a tag + BOOST_LOG_SCOPED_THREAD_TAG("Tag", "IMPORTANT MESSAGES"); + + // We may omit the severity and use the shorter BOOST_LOG macro. The logger "slg" + // has the default severity that may be specified on its construction. We didn't + // do it, so it is 0 by default. Therefore this record will have "normal" severity. + // The only reason this record will be output is the "Tag" attribute we added above. + BOOST_LOG(slg) << "Some really urgent line"; + } + + pSink->reset_filter(); + + // And moreover, it is possible to nest logging records. For example, this will + // be processed in the order of evaluation: + BOOST_LOG(lg) << "The result of foo is " << foo(lg); + + return 0; +} diff --git a/src/boost/libs/log/example/async_log/Jamfile.v2 b/src/boost/libs/log/example/async_log/Jamfile.v2 new file mode 100644 index 00000000..bb2438d4 --- /dev/null +++ b/src/boost/libs/log/example/async_log/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe async_log + : main.cpp + ; diff --git a/src/boost/libs/log/example/async_log/main.cpp b/src/boost/libs/log/example/async_log/main.cpp new file mode 100644 index 00000000..26763a4b --- /dev/null +++ b/src/boost/libs/log/example/async_log/main.cpp @@ -0,0 +1,129 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 30.08.2009 + * + * \brief An example of asynchronous logging in multiple threads. + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <stdexcept> +#include <string> +#include <iostream> +#include <fstream> +#include <functional> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/barrier.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks.hpp> +#include <boost/log/sources/logger.hpp> +#include <boost/log/utility/record_ordering.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +enum +{ + LOG_RECORDS_TO_WRITE = 10000, + THREAD_COUNT = 2 +}; + +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt) + +//! This function is executed in multiple threads +void thread_fun(boost::barrier& bar) +{ + // Wait until all threads are created + bar.wait(); + + // Here we go. First, identify the thread. + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + + // Now, do some logging + for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) + { + BOOST_LOG(test_lg::get()) << "Log record " << i; + } +} + +int main(int argc, char* argv[]) +{ + try + { + // Open a rotating text file + shared_ptr< std::ostream > strm(new std::ofstream("test.log")); + if (!strm->good()) + throw std::runtime_error("Failed to open a text log file"); + + // Create a text file sink + typedef sinks::text_ostream_backend backend_t; + typedef sinks::asynchronous_sink< + backend_t, + sinks::unbounded_ordering_queue< + logging::attribute_value_ordering< unsigned int, std::less< unsigned int > > + > + > sink_t; + shared_ptr< sink_t > sink(new sink_t( + boost::make_shared< backend_t >(), + // We'll apply record ordering to ensure that records from different threads go sequentially in the file + keywords::order = logging::make_attr_ordering("RecordID", std::less< unsigned int >()))); + + sink->locked_backend()->add_stream(strm); + + sink->set_formatter + ( + expr::format("%1%: [%2%] [%3%] - %4%") + % expr::attr< unsigned int >("RecordID") + % expr::attr< boost::posix_time::ptime >("TimeStamp") + % expr::attr< boost::thread::id >("ThreadID") + % expr::smessage + ); + + // Add it to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + + // Create logging threads + boost::barrier bar(THREAD_COUNT); + boost::thread_group threads; + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads.create_thread(boost::bind(&thread_fun, boost::ref(bar))); + + // Wait until all action ends + threads.join_all(); + + // Flush all buffered records + sink->stop(); + sink->flush(); + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/basic_usage/Jamfile.v2 b/src/boost/libs/log/example/basic_usage/Jamfile.v2 new file mode 100644 index 00000000..56a9d5eb --- /dev/null +++ b/src/boost/libs/log/example/basic_usage/Jamfile.v2 @@ -0,0 +1,58 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/log//boost_log_setup + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/thread//boost_thread + ; + +exe basic_usage + : main.cpp + ; diff --git a/src/boost/libs/log/example/basic_usage/main.cpp b/src/boost/libs/log/example/basic_usage/main.cpp new file mode 100644 index 00000000..3dc8a5af --- /dev/null +++ b/src/boost/libs/log/example/basic_usage/main.cpp @@ -0,0 +1,129 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 11.11.2007 + * + * \brief An example of basic library usage. See the library tutorial for expanded + * comments on this code. It may also be worthwhile reading the Wiki requirements page: + * http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging + */ + +// #define BOOST_LOG_USE_CHAR +// #define BOOST_ALL_DYN_LINK 1 +// #define BOOST_LOG_DYN_LINK 1 + +#include <iostream> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> + +#include <boost/log/utility/setup/file.hpp> +#include <boost/log/utility/setup/console.hpp> +#include <boost/log/utility/setup/common_attributes.hpp> + +#include <boost/log/attributes/timer.hpp> +#include <boost/log/attributes/named_scope.hpp> + +#include <boost/log/sources/logger.hpp> + +#include <boost/log/support/date_time.hpp> + +namespace logging = boost::log; +namespace sinks = boost::log::sinks; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +// Here we define our application severity levels. +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +// The formatting logic for the severity level +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< ( + std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl) +{ + static const char* const str[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str))) + strm << str[lvl]; + else + strm << static_cast< int >(lvl); + return strm; +} + +int main(int argc, char* argv[]) +{ + // This is a simple tutorial/example of Boost.Log usage + + // The first thing we have to do to get using the library is + // to set up the logging sinks - i.e. where the logs will be written to. + logging::add_console_log(std::clog, keywords::format = "%TimeStamp%: %Message%"); + + // One can also use lambda expressions to setup filters and formatters + logging::add_file_log + ( + "sample.log", + keywords::filter = expr::attr< severity_level >("Severity") >= warning, + keywords::format = expr::stream + << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d, %H:%M:%S.%f") + << " [" << expr::format_date_time< attrs::timer::value_type >("Uptime", "%O:%M:%S") + << "] [" << expr::format_named_scope("Scope", keywords::format = "%n (%f:%l)") + << "] <" << expr::attr< severity_level >("Severity") + << "> " << expr::message +/* + keywords::format = expr::format("%1% [%2%] [%3%] <%4%> %5%") + % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d, %H:%M:%S.%f") + % expr::format_date_time< attrs::timer::value_type >("Uptime", "%O:%M:%S") + % expr::format_named_scope("Scope", keywords::format = "%n (%f:%l)") + % expr::attr< severity_level >("Severity") + % expr::message +*/ + ); + + // Also let's add some commonly used attributes, like timestamp and record counter. + logging::add_common_attributes(); + logging::core::get()->add_thread_attribute("Scope", attrs::named_scope()); + + BOOST_LOG_FUNCTION(); + + // Now our logs will be written both to the console and to the file. + // Let's do a quick test and output something. We have to create a logger for this. + src::logger lg; + + // And output... + BOOST_LOG(lg) << "Hello, World!"; + + // Now, let's try logging with severity + src::severity_logger< severity_level > slg; + + // Let's pretend we also want to profile our code, so add a special timer attribute. + slg.add_attribute("Uptime", attrs::timer()); + + BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the file"; + BOOST_LOG_SEV(slg, warning) << "A warning severity message, will pass to the file"; + BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the file"; + + return 0; +} diff --git a/src/boost/libs/log/example/bounded_async_log/Jamfile.v2 b/src/boost/libs/log/example/bounded_async_log/Jamfile.v2 new file mode 100644 index 00000000..36e47a29 --- /dev/null +++ b/src/boost/libs/log/example/bounded_async_log/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe bounded_async_log + : main.cpp + ; diff --git a/src/boost/libs/log/example/bounded_async_log/main.cpp b/src/boost/libs/log/example/bounded_async_log/main.cpp new file mode 100644 index 00000000..eefeb52a --- /dev/null +++ b/src/boost/libs/log/example/bounded_async_log/main.cpp @@ -0,0 +1,131 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 30.08.2009 + * + * \brief An example of asynchronous logging with bounded log record queue in multiple threads. + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <stdexcept> +#include <string> +#include <iostream> +#include <fstream> +#include <functional> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/barrier.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks.hpp> +#include <boost/log/sources/logger.hpp> +#include <boost/log/utility/record_ordering.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +enum +{ + LOG_RECORDS_TO_WRITE = 10000, + THREAD_COUNT = 2 +}; + +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt) + +//! This function is executed in multiple threads +void thread_fun(boost::barrier& bar) +{ + // Wait until all threads are created + bar.wait(); + + // Here we go. First, identify the thread. + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + + // Now, do some logging + for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) + { + BOOST_LOG(test_lg::get()) << "Log record " << i; + } +} + +int main(int argc, char* argv[]) +{ + try + { + // Open a rotating text file + shared_ptr< std::ostream > strm(new std::ofstream("test.log")); + if (!strm->good()) + throw std::runtime_error("Failed to open a text log file"); + + // Create a text file sink + typedef sinks::text_ostream_backend backend_t; + typedef sinks::asynchronous_sink< + backend_t, + sinks::bounded_ordering_queue< + logging::attribute_value_ordering< unsigned int, std::less< unsigned int > >, + 128, // queue no more than 128 log records + sinks::block_on_overflow // wait until records are processed + > + > sink_t; + shared_ptr< sink_t > sink(new sink_t( + boost::make_shared< backend_t >(), + // We'll apply record ordering to ensure that records from different threads go sequentially in the file + keywords::order = logging::make_attr_ordering("RecordID", std::less< unsigned int >()))); + + sink->locked_backend()->add_stream(strm); + + sink->set_formatter + ( + expr::format("%1%: [%2%] [%3%] - %4%") + % expr::attr< unsigned int >("RecordID") + % expr::attr< boost::posix_time::ptime >("TimeStamp") + % expr::attr< boost::thread::id >("ThreadID") + % expr::smessage + ); + + // Add it to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + + // Create logging threads + boost::barrier bar(THREAD_COUNT); + boost::thread_group threads; + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads.create_thread(boost::bind(&thread_fun, boost::ref(bar))); + + // Wait until all action ends + threads.join_all(); + + // Flush all buffered records + sink->stop(); + sink->flush(); + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/event_log/Jamfile.v2 b/src/boost/libs/log/example/event_log/Jamfile.v2 new file mode 100644 index 00000000..bcc17f46 --- /dev/null +++ b/src/boost/libs/log/example/event_log/Jamfile.v2 @@ -0,0 +1,102 @@ +# +# Copyright Andrey Semashev 2007 - 2016. +# 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) +# + +import os ; +import configure ; +import ../../build/log-platform-config ; + +rule has-config-flag ( flag : properties * ) +{ + if ( "<define>$(flag)" in $(properties) || "<define>$(flag)=1" in $(properties) ) + { + return 1 ; + } + else + { + return ; + } +} + +rule check-message-compiler ( properties * ) +{ + local result ; + + if <target-os>windows in $(properties) + { + if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] + { + local has_mc = [ configure.builds /boost/log/message-compiler//test-availability : $(properties) : message-compiler ] ; + if ! $(has_mc) + { + result += <build>no ; + } + } + } + else + { + result += <build>no ; + } + + return $(result) ; +} + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + <conditional>@check-message-compiler + + <link>shared + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +lib event_log_messages + : event_log_messages.mc + : <linkflags>-noentry +# <name>event_log_messages + ; + +exe event_log + : main.cpp + : <implicit-dependency>event_log_messages + ; diff --git a/src/boost/libs/log/example/event_log/event_log_messages.mc b/src/boost/libs/log/example/event_log/event_log_messages.mc new file mode 100644 index 00000000..d3e10790 --- /dev/null +++ b/src/boost/libs/log/example/event_log/event_log_messages.mc @@ -0,0 +1,58 @@ +; /* -------------------------------------------------------- +; HEADER SECTION +; */ +SeverityNames=(Debug=0x0:MY_SEVERITY_DEBUG + Info=0x1:MY_SEVERITY_INFO + Warning=0x2:MY_SEVERITY_WARNING + Error=0x3:MY_SEVERITY_ERROR + ) + +; /* -------------------------------------------------------- +; MESSAGE DEFINITION SECTION +; */ + +MessageIdTypedef=WORD + +MessageId=0x1 +SymbolicName=MY_CATEGORY_1 +Language=English +Category 1 +. + +MessageId=0x2 +SymbolicName=MY_CATEGORY_2 +Language=English +Category 2 +. + +MessageId=0x3 +SymbolicName=MY_CATEGORY_3 +Language=English +Category 3 +. + +MessageIdTypedef=DWORD + +MessageId=0x100 +Severity=Warning +Facility=Application +SymbolicName=LOW_DISK_SPACE_MSG +Language=English +The drive %1 has low free disk space. At least %2 Mb of free space is recommended. +. + +MessageId=0x101 +Severity=Error +Facility=Application +SymbolicName=DEVICE_INACCESSIBLE_MSG +Language=English +The drive %1 is not accessible. +. + +MessageId=0x102 +Severity=Info +Facility=Application +SymbolicName=SUCCEEDED_MSG +Language=English +Operation finished successfully in %1 seconds. +. diff --git a/src/boost/libs/log/example/event_log/main.cpp b/src/boost/libs/log/example/event_log/main.cpp new file mode 100644 index 00000000..ca02a188 --- /dev/null +++ b/src/boost/libs/log/example/event_log/main.cpp @@ -0,0 +1,190 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 16.11.2008 + * + * \brief An example of logging into Windows event log. + * + * The example shows the basic usage of the Windows NT event log backend. + * The code defines custom severity levels, initializes the sink and a couple of + * attributes to test with, and writes several records at different levels. + * As a result the written records should appear in the Application log, and + * should be displayed correctly with the Windows event log viewer. + */ + +#include <stdexcept> +#include <string> +#include <iostream> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/sinks/sync_frontend.hpp> +#include <boost/log/sinks/event_log_backend.hpp> + +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include <windows.h> +#include "event_log_messages.h" + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +//[ example_sinks_event_log_severity +// Define application-specific severity levels +enum severity_level +{ + normal, + warning, + error +}; +//] + +void init_logging() +{ + //[ example_sinks_event_log_create_backend + // Create an event log sink + boost::shared_ptr< sinks::event_log_backend > backend( + new sinks::event_log_backend(( + keywords::message_file = "%SystemDir%\\event_log_messages.dll", + keywords::log_name = "My Application", + keywords::log_source = "My Source" + )) + ); + //] + + //[ example_sinks_event_log_event_composer + // Create an event composer. It is initialized with the event identifier mapping. + sinks::event_log::event_composer composer( + sinks::event_log::direct_event_id_mapping< int >("EventID")); + + // For each event described in the message file, set up the insertion string formatters + composer[LOW_DISK_SPACE_MSG] + // the first placeholder in the message + // will be replaced with contents of the "Drive" attribute + % expr::attr< std::string >("Drive") + // the second placeholder in the message + // will be replaced with contents of the "Size" attribute + % expr::attr< boost::uintmax_t >("Size"); + + composer[DEVICE_INACCESSIBLE_MSG] + % expr::attr< std::string >("Drive"); + + composer[SUCCEEDED_MSG] + % expr::attr< unsigned int >("Duration"); + + // Then put the composer to the backend + backend->set_event_composer(composer); + //] + + //[ example_sinks_event_log_mappings + // We'll have to map our custom levels to the event log event types + sinks::event_log::custom_event_type_mapping< severity_level > type_mapping("Severity"); + type_mapping[normal] = sinks::event_log::make_event_type(MY_SEVERITY_INFO); + type_mapping[warning] = sinks::event_log::make_event_type(MY_SEVERITY_WARNING); + type_mapping[error] = sinks::event_log::make_event_type(MY_SEVERITY_ERROR); + + backend->set_event_type_mapper(type_mapping); + + // Same for event categories. + // Usually event categories can be restored by the event identifier. + sinks::event_log::custom_event_category_mapping< int > cat_mapping("EventID"); + cat_mapping[LOW_DISK_SPACE_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_1); + cat_mapping[DEVICE_INACCESSIBLE_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_2); + cat_mapping[SUCCEEDED_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_3); + + backend->set_event_category_mapper(cat_mapping); + //] + + //[ example_sinks_event_log_register_sink + // Create the frontend for the sink + boost::shared_ptr< sinks::synchronous_sink< sinks::event_log_backend > > sink( + new sinks::synchronous_sink< sinks::event_log_backend >(backend)); + + // Set up filter to pass only records that have the necessary attribute + sink->set_filter(expr::has_attr< int >("EventID")); + + logging::core::get()->add_sink(sink); + //] +} + +//[ example_sinks_event_log_facilities +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(event_logger, src::severity_logger_mt< severity_level >) + +// The function raises an event of the disk space depletion +void announce_low_disk_space(std::string const& drive, boost::uintmax_t size) +{ + BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)LOW_DISK_SPACE_MSG); + BOOST_LOG_SCOPED_THREAD_TAG("Drive", drive); + BOOST_LOG_SCOPED_THREAD_TAG("Size", size); + // Since this record may get accepted by other sinks, + // this message is not completely useless + BOOST_LOG_SEV(event_logger::get(), warning) << "Low disk " << drive + << " space, " << size << " Mb is recommended"; +} + +// The function raises an event of inaccessible disk drive +void announce_device_inaccessible(std::string const& drive) +{ + BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)DEVICE_INACCESSIBLE_MSG); + BOOST_LOG_SCOPED_THREAD_TAG("Drive", drive); + BOOST_LOG_SEV(event_logger::get(), error) << "Cannot access drive " << drive; +} + +// The structure is an activity guard that will emit an event upon the activity completion +struct activity_guard +{ + activity_guard() + { + // Add a stop watch attribute to measure the activity duration + m_it = event_logger::get().add_attribute("Duration", attrs::timer()).first; + } + ~activity_guard() + { + BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)SUCCEEDED_MSG); + BOOST_LOG_SEV(event_logger::get(), normal) << "Activity ended"; + event_logger::get().remove_attribute(m_it); + } + +private: + logging::attribute_set::iterator m_it; +}; +//] + +int main(int argc, char* argv[]) +{ + try + { + // Initialize the library + init_logging(); + + // Make some events + { + activity_guard activity; + + announce_low_disk_space("C:", 2 * 1024 * 1024); + announce_device_inaccessible("D:"); + } + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/keywords/Jamfile.v2 b/src/boost/libs/log/example/keywords/Jamfile.v2 new file mode 100644 index 00000000..54db6e11 --- /dev/null +++ b/src/boost/libs/log/example/keywords/Jamfile.v2 @@ -0,0 +1,58 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/log//boost_log_setup + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/thread//boost_thread + ; + +exe keywords + : main.cpp + ; diff --git a/src/boost/libs/log/example/keywords/main.cpp b/src/boost/libs/log/example/keywords/main.cpp new file mode 100644 index 00000000..5271f95a --- /dev/null +++ b/src/boost/libs/log/example/keywords/main.cpp @@ -0,0 +1,133 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 01.12.2012 + * + * \brief An example of using attribute keywords. + */ + +// #define BOOST_LOG_USE_CHAR +// #define BOOST_ALL_DYN_LINK 1 +// #define BOOST_LOG_DYN_LINK 1 + +#include <iostream> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> + +#include <boost/log/utility/setup/file.hpp> +#include <boost/log/utility/setup/console.hpp> +#include <boost/log/utility/setup/common_attributes.hpp> + +#include <boost/log/attributes/timer.hpp> +#include <boost/log/attributes/named_scope.hpp> + +#include <boost/log/sources/logger.hpp> + +#include <boost/log/support/date_time.hpp> + +namespace logging = boost::log; +namespace sinks = boost::log::sinks; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +// Here we define our application severity levels. +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +// The formatting logic for the severity level +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< ( + std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl) +{ + static const char* const str[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str))) + strm << str[lvl]; + else + strm << static_cast< int >(lvl); + return strm; +} + +// Declare attribute keywords +BOOST_LOG_ATTRIBUTE_KEYWORD(_severity, "Severity", severity_level) +BOOST_LOG_ATTRIBUTE_KEYWORD(_timestamp, "TimeStamp", boost::posix_time::ptime) +BOOST_LOG_ATTRIBUTE_KEYWORD(_uptime, "Uptime", attrs::timer::value_type) +BOOST_LOG_ATTRIBUTE_KEYWORD(_scope, "Scope", attrs::named_scope::value_type) + +int main(int argc, char* argv[]) +{ + // This is a simple tutorial/example of Boost.Log usage + + // The first thing we have to do to get using the library is + // to set up the logging sinks - i.e. where the logs will be written to. + logging::add_console_log(std::clog, keywords::format = "%TimeStamp%: %_%"); + + // One can also use lambda expressions to setup filters and formatters + logging::add_file_log + ( + "sample.log", + keywords::filter = _severity >= warning, + keywords::format = expr::stream + << expr::format_date_time(_timestamp, "%Y-%m-%d, %H:%M:%S.%f") + << " [" << expr::format_date_time(_uptime, "%O:%M:%S") + << "] [" << expr::format_named_scope(_scope, keywords::format = "%n (%f:%l)") + << "] <" << _severity + << "> " << expr::message +/* + keywords::format = expr::format("%1% [%2%] [%3%] <%4%> %5%") + % expr::format_date_time(_timestamp, "%Y-%m-%d, %H:%M:%S.%f") + % expr::format_date_time(_uptime, "%O:%M:%S") + % expr::format_named_scope(_scope, keywords::format = "%n (%f:%l)") + % _severity + % expr::message +*/ + ); + + // Also let's add some commonly used attributes, like timestamp and record counter. + logging::add_common_attributes(); + logging::core::get()->add_thread_attribute("Scope", attrs::named_scope()); + + BOOST_LOG_FUNCTION(); + + // Now our logs will be written both to the console and to the file. + // Let's do a quick test and output something. We have to create a logger for this. + src::logger lg; + + // And output... + BOOST_LOG(lg) << "Hello, World!"; + + // Now, let's try logging with severity + src::severity_logger< severity_level > slg; + + // Let's pretend we also want to profile our code, so add a special timer attribute. + slg.add_attribute("Uptime", attrs::timer()); + + BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the file"; + BOOST_LOG_SEV(slg, warning) << "A warning severity message, will pass to the file"; + BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the file"; + + return 0; +} diff --git a/src/boost/libs/log/example/multiple_files/Jamfile.v2 b/src/boost/libs/log/example/multiple_files/Jamfile.v2 new file mode 100644 index 00000000..81e3e5dd --- /dev/null +++ b/src/boost/libs/log/example/multiple_files/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe multiple_files + : main.cpp + ; diff --git a/src/boost/libs/log/example/multiple_files/main.cpp b/src/boost/libs/log/example/multiple_files/main.cpp new file mode 100644 index 00000000..0a1ce47d --- /dev/null +++ b/src/boost/libs/log/example/multiple_files/main.cpp @@ -0,0 +1,105 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 26.04.2008 + * + * \brief This example shows how to perform logging to several files simultaneously, + * with files being created on an attribute value basis - thread identifier in this case. + * In the example the application creates a number of threads and registers thread + * identifiers as attributes. Every thread performs logging, and the sink separates + * log records from different threads into different files. + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <stdexcept> +#include <string> +#include <iostream> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/thread/thread.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sources/logger.hpp> +#include <boost/log/sinks/sync_frontend.hpp> +#include <boost/log/sinks/text_multifile_backend.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +enum +{ + THREAD_COUNT = 5, + LOG_RECORDS_TO_WRITE = 10 +}; + +// Global logger declaration +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt) + +// This function is executed in a separate thread +void thread_foo() +{ + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) + { + BOOST_LOG(my_logger::get()) << "Log record " << i; + } +} + +int main(int argc, char* argv[]) +{ + try + { + // Create a text file sink + typedef sinks::synchronous_sink< sinks::text_multifile_backend > file_sink; + shared_ptr< file_sink > sink(new file_sink); + + // Set up how the file names will be generated + sink->locked_backend()->set_file_name_composer(sinks::file::as_file_name_composer( + expr::stream << "logs/" << expr::attr< boost::thread::id >("ThreadID") << ".log")); + + // Set the log record formatter + sink->set_formatter + ( + expr::format("%1%: [%2%] - %3%") + % expr::attr< unsigned int >("RecordID") + % expr::attr< boost::posix_time::ptime >("TimeStamp") + % expr::smessage + ); + + // Add it to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + + // Create threads and make some logs + boost::thread_group threads; + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads.create_thread(&thread_foo); + + threads.join_all(); + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/multiple_threads/Jamfile.v2 b/src/boost/libs/log/example/multiple_threads/Jamfile.v2 new file mode 100644 index 00000000..269a0cb7 --- /dev/null +++ b/src/boost/libs/log/example/multiple_threads/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe multiple_threads + : main.cpp + ; diff --git a/src/boost/libs/log/example/multiple_threads/main.cpp b/src/boost/libs/log/example/multiple_threads/main.cpp new file mode 100644 index 00000000..a9b036fc --- /dev/null +++ b/src/boost/libs/log/example/multiple_threads/main.cpp @@ -0,0 +1,115 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 10.06.2008 + * + * \brief An example of logging in multiple threads. + * See the library tutorial for expanded comments on this code. + * It may also be worthwhile reading the Wiki requirements page: + * http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <stdexcept> +#include <string> +#include <iostream> +#include <fstream> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/barrier.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks.hpp> +#include <boost/log/sources/logger.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +enum +{ + LOG_RECORDS_TO_WRITE = 10000, + THREAD_COUNT = 2 +}; + +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt) + +//! This function is executed in multiple threads +void thread_fun(boost::barrier& bar) +{ + // Wait until all threads are created + bar.wait(); + + // Now, do some logging + for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) + { + BOOST_LOG(test_lg::get()) << "Log record " << i; + } +} + +int main(int argc, char* argv[]) +{ + try + { + // Open a rotating text file + shared_ptr< std::ostream > strm(new std::ofstream("test.log")); + if (!strm->good()) + throw std::runtime_error("Failed to open a text log file"); + + // Create a text file sink + shared_ptr< sinks::synchronous_sink< sinks::text_ostream_backend > > sink( + new sinks::synchronous_sink< sinks::text_ostream_backend >); + + sink->locked_backend()->add_stream(strm); + + sink->set_formatter + ( + expr::format("%1%: [%2%] [%3%] - %4%") + % expr::attr< unsigned int >("RecordID") + % expr::attr< boost::posix_time::ptime >("TimeStamp") + % expr::attr< attrs::current_thread_id::value_type >("ThreadID") + % expr::smessage + ); + + // Add it to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id()); + + // Create logging threads + boost::barrier bar(THREAD_COUNT); + boost::thread_group threads; + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads.create_thread(boost::bind(&thread_fun, boost::ref(bar))); + + // Wait until all action ends + threads.join_all(); + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/native_syslog/Jamfile.v2 b/src/boost/libs/log/example/native_syslog/Jamfile.v2 new file mode 100644 index 00000000..51ee1c2e --- /dev/null +++ b/src/boost/libs/log/example/native_syslog/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe native_syslog + : main.cpp + ; diff --git a/src/boost/libs/log/example/native_syslog/main.cpp b/src/boost/libs/log/example/native_syslog/main.cpp new file mode 100644 index 00000000..d9814d52 --- /dev/null +++ b/src/boost/libs/log/example/native_syslog/main.cpp @@ -0,0 +1,109 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 07.03.2009 + * + * \brief An example of logging to a syslog server (syslogd, for example). + * + * The example shows how to initialize logging to a local syslog server. + * The code creates a sink that will use native syslog API to emit messages. + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <boost/config.hpp> +#if !defined(BOOST_WINDOWS) +#define BOOST_LOG_USE_NATIVE_SYSLOG +#endif + +#include <stdexcept> +#include <string> +#include <iostream> +#include <boost/smart_ptr/shared_ptr.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks/sync_frontend.hpp> +#include <boost/log/sinks/syslog_backend.hpp> + +#if defined(BOOST_LOG_USE_NATIVE_SYSLOG) + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +//! Define application-specific severity levels +enum severity_levels +{ + normal, + warning, + error +}; + +int main(int argc, char* argv[]) +{ + try + { + // Create a syslog sink + shared_ptr< sinks::synchronous_sink< sinks::syslog_backend > > sink( + new sinks::synchronous_sink< sinks::syslog_backend >( + keywords::use_impl = sinks::syslog::native, + keywords::facility = sinks::syslog::local7)); + + sink->set_formatter + ( + expr::format("native_syslog: %1%: %2%") + % expr::attr< unsigned int >("RecordID") + % expr::smessage + ); + + // We'll have to map our custom levels to the syslog levels + sinks::syslog::custom_severity_mapping< severity_levels > mapping("Severity"); + mapping[normal] = sinks::syslog::info; + mapping[warning] = sinks::syslog::warning; + mapping[error] = sinks::syslog::critical; + + sink->locked_backend()->set_severity_mapper(mapping); + + // Add the sink to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + + // Do some logging + src::severity_logger< severity_levels > lg(keywords::severity = normal); + BOOST_LOG_SEV(lg, normal) << "A syslog record with normal level"; + BOOST_LOG_SEV(lg, warning) << "A syslog record with warning level"; + BOOST_LOG_SEV(lg, error) << "A syslog record with error level"; + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} + +#else // defined(BOOST_LOG_USE_NATIVE_SYSLOG) + +int main (int, char*[]) +{ + std::cout << "Native syslog API is not supported on this platform" << std::endl; + return 0; +} + +#endif // defined(BOOST_LOG_USE_NATIVE_SYSLOG) diff --git a/src/boost/libs/log/example/rotating_file/Jamfile.v2 b/src/boost/libs/log/example/rotating_file/Jamfile.v2 new file mode 100644 index 00000000..f91c942e --- /dev/null +++ b/src/boost/libs/log/example/rotating_file/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe rotating_file + : main.cpp + ; diff --git a/src/boost/libs/log/example/rotating_file/main.cpp b/src/boost/libs/log/example/rotating_file/main.cpp new file mode 100644 index 00000000..bb221f6e --- /dev/null +++ b/src/boost/libs/log/example/rotating_file/main.cpp @@ -0,0 +1,96 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 26.04.2008 + * + * \brief An example of logging into a rotating text file. + * See the library tutorial for expanded comments on this code. + * It may also be worthwhile reading the Wiki requirements page: + * http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <stdexcept> +#include <string> +#include <iostream> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sources/logger.hpp> +#include <boost/log/sinks/sync_frontend.hpp> +#include <boost/log/sinks/text_file_backend.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +enum { LOG_RECORDS_TO_WRITE = 10000 }; + +int main(int argc, char* argv[]) +{ + try + { + // Create a text file sink + typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink; + shared_ptr< file_sink > sink(new file_sink( + keywords::file_name = "file.log", // file name pattern + keywords::target_file_name = "%Y%m%d_%H%M%S_%5N.log", // file name pattern + keywords::rotation_size = 16384 // rotation size, in characters + )); + + // Set up where the rotated files will be stored + sink->locked_backend()->set_file_collector(sinks::file::make_collector( + keywords::target = "logs", // where to store rotated files + keywords::max_size = 16 * 1024 * 1024, // maximum total size of the stored files, in bytes + keywords::min_free_space = 100 * 1024 * 1024, // minimum free space on the drive, in bytes + keywords::max_files = 512 // maximum number of stored files + )); + + // Upon restart, scan the target directory for files matching the file_name pattern + sink->locked_backend()->scan_for_files(); + + sink->set_formatter + ( + expr::format("%1%: [%2%] - %3%") + % expr::attr< unsigned int >("RecordID") + % expr::attr< boost::posix_time::ptime >("TimeStamp") + % expr::smessage + ); + + // Add it to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + + // Do some logging + src::logger lg; + for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) + { + BOOST_LOG(lg) << "Some log record"; + } + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/settings_file/Jamfile.v2 b/src/boost/libs/log/example/settings_file/Jamfile.v2 new file mode 100644 index 00000000..8459f7d7 --- /dev/null +++ b/src/boost/libs/log/example/settings_file/Jamfile.v2 @@ -0,0 +1,58 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/log//boost_log_setup + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/thread//boost_thread + ; + +exe settings_file + : main.cpp + ; diff --git a/src/boost/libs/log/example/settings_file/main.cpp b/src/boost/libs/log/example/settings_file/main.cpp new file mode 100644 index 00000000..eb4a13f9 --- /dev/null +++ b/src/boost/libs/log/example/settings_file/main.cpp @@ -0,0 +1,74 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 26.04.2008 + * + * \brief An example of initializing the library from a settings file. + * See the library tutorial for expanded comments on this code. + * It may also be worthwhile reading the Wiki requirements page: + * http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging + */ + +// #define BOOST_ALL_DYN_LINK 1 + +#include <exception> +#include <iostream> +#include <fstream> + +#include <boost/log/trivial.hpp> +#include <boost/log/common.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/utility/setup/from_stream.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; + +void try_logging() +{ + BOOST_LOG_TRIVIAL(trace) << "This is a trace severity record"; + BOOST_LOG_TRIVIAL(debug) << "This is a debug severity record"; + BOOST_LOG_TRIVIAL(info) << "This is an info severity record"; + BOOST_LOG_TRIVIAL(warning) << "This is a warning severity record"; + BOOST_LOG_TRIVIAL(error) << "This is an error severity record"; + BOOST_LOG_TRIVIAL(fatal) << "This is a fatal severity record"; +} + +int main(int argc, char* argv[]) +{ + try + { + // Open the file + std::ifstream settings("settings.txt"); + if (!settings.is_open()) + { + std::cout << "Could not open settings.txt file" << std::endl; + return 1; + } + + // Read the settings and initialize logging library + logging::init_from_stream(settings); + + // Add some attributes + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + + // Try logging + try_logging(); + + // Now enable tagging and try again + BOOST_LOG_SCOPED_THREAD_TAG("Tag", "TAGGED"); + try_logging(); + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/settings_file/settings.txt b/src/boost/libs/log/example/settings_file/settings.txt new file mode 100644 index 00000000..c9fdb9db --- /dev/null +++ b/src/boost/libs/log/example/settings_file/settings.txt @@ -0,0 +1,25 @@ +# +# Copyright Andrey Semashev 2007 - 2014. +# 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) +# + +[Core] + +Filter="%Severity% >= debug" + + +[Sinks.1] + +Destination=Console +Format="%TimeStamp% [%Severity%] *** %Message%" +Filter="%Tag% | %Severity% > info" + + +[Sinks.2] + +Destination=TextFile +FileName=test.log +AutoFlush=true +Format="[%TimeStamp%] [%Severity%] [%Tag%] %Message%" diff --git a/src/boost/libs/log/example/settings_file_custom_factories/Jamfile.v2 b/src/boost/libs/log/example/settings_file_custom_factories/Jamfile.v2 new file mode 100644 index 00000000..8032af9b --- /dev/null +++ b/src/boost/libs/log/example/settings_file_custom_factories/Jamfile.v2 @@ -0,0 +1,58 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/log//boost_log_setup + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/thread//boost_thread + ; + +exe settings_file_custom_factories + : main.cpp + ; diff --git a/src/boost/libs/log/example/settings_file_custom_factories/main.cpp b/src/boost/libs/log/example/settings_file_custom_factories/main.cpp new file mode 100644 index 00000000..3d59444b --- /dev/null +++ b/src/boost/libs/log/example/settings_file_custom_factories/main.cpp @@ -0,0 +1,205 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 12.05.2010 + * + * \brief An example of initializing the library from a settings file, + * with custom filter and formatter factories for attributes. + */ + +// #define BOOST_ALL_DYN_LINK 1 + +#include <string> +#include <iostream> +#include <fstream> +#include <stdexcept> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/log/core.hpp> +#include <boost/log/common.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/utility/setup/from_stream.hpp> +#include <boost/log/utility/setup/common_attributes.hpp> +#include <boost/log/utility/setup/filter_parser.hpp> +#include <boost/log/utility/setup/formatter_parser.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; + +//! Enum for our custom severity levels +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +//! Formatting operator for severity levels +inline std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + switch (level) + { + case normal: + strm << "normal"; + break; + case notification: + strm << "notification"; + break; + case warning: + strm << "warning"; + break; + case error: + strm << "error"; + break; + case critical: + strm << "critical"; + break; + default: + strm << static_cast< int >(level); + break; + } + + return strm; +} + +//! Parsing operator for severity levels +inline std::istream& operator>> (std::istream& strm, severity_level& level) +{ + if (strm.good()) + { + std::string str; + strm >> str; + if (str == "normal") + level = normal; + else if (str == "notification") + level = notification; + else if (str == "warning") + level = warning; + else if (str == "error") + level = error; + else if (str == "critical") + level = critical; + else + strm.setstate(std::ios_base::failbit); + } + + return strm; +} + +//! Our custom formatter for the scope list +struct scope_list_formatter +{ + typedef void result_type; + typedef attrs::named_scope::value_type scope_stack; + + explicit scope_list_formatter(logging::attribute_name const& name) : + name_(name) + { + } + void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const + { + // We need to acquire the attribute value from the log record + logging::visit< scope_stack > + ( + name_, + rec.attribute_values(), + boost::bind(&scope_list_formatter::format, _1, boost::ref(strm)) + ); + } + +private: + //! This is where our custom formatting takes place + static void format(scope_stack const& scopes, logging::formatting_ostream& strm) + { + scope_stack::const_iterator it = scopes.begin(), end = scopes.end(); + for (; it != end; ++it) + { + strm << "\t" << it->scope_name << " [" << it->file_name << ":" << it->line << "]\n"; + } + } + +private: + logging::attribute_name name_; +}; + +class my_scopes_formatter_factory : + public logging::formatter_factory< char > +{ +public: + /*! + * This function creates a formatter for the MyScopes attribute. + * It effectively associates the attribute with the scope_list_formatter class + */ + formatter_type create_formatter( + logging::attribute_name const& attr_name, args_map const& args) + { + return formatter_type(scope_list_formatter(attr_name)); + } +}; + +//! The function initializes the logging library +void init_logging() +{ + // First thing - register the custom formatter for MyScopes + logging::register_formatter_factory("MyScopes", boost::make_shared< my_scopes_formatter_factory >()); + + // Also register filter and formatter factories for our custom severity level enum. Since our operator<< and operator>> implement + // all required behavior, simple factories provided by Boost.Log will do. + logging::register_simple_filter_factory< severity_level >("Severity"); + logging::register_simple_formatter_factory< severity_level, char >("Severity"); + + // Then load the settings from the file + std::ifstream settings("settings.txt"); + if (!settings.is_open()) + throw std::runtime_error("Could not open settings.txt file"); + logging::init_from_stream(settings); + + // Add some attributes. Note that severity level will be provided by the logger, so we don't need to add it here. + logging::add_common_attributes(); + + logging::core::get()->add_global_attribute("MyScopes", attrs::named_scope()); +} + +//! Global logger, which we will use to write log messages +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::severity_logger< severity_level >) + +//! The function tests logging +void try_logging() +{ + BOOST_LOG_FUNCTION(); + + src::severity_logger< severity_level >& lg = test_lg::get(); + + BOOST_LOG_SEV(lg, critical) << "This is a critical severity record"; + + BOOST_LOG_NAMED_SCOPE("random name"); + BOOST_LOG_SEV(lg, error) << "This is a error severity record"; +} + +int main(int argc, char* argv[]) +{ + try + { + init_logging(); + try_logging(); + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return -1; + } + + return 0; +} diff --git a/src/boost/libs/log/example/settings_file_custom_factories/settings.txt b/src/boost/libs/log/example/settings_file_custom_factories/settings.txt new file mode 100644 index 00000000..98d8d7fd --- /dev/null +++ b/src/boost/libs/log/example/settings_file_custom_factories/settings.txt @@ -0,0 +1,14 @@ +# +# Copyright Andrey Semashev 2007 - 2014. +# 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) +# + +[Sinks.TextFileSettings] +Destination=TextFile +FileName=test.log +AutoFlush=true +Filter="%Severity% > normal" +Format="[%TimeStamp%] [%Severity%]\n%MyScopes%\n\t:: %Message%" +Asynchronous=false diff --git a/src/boost/libs/log/example/syslog/Jamfile.v2 b/src/boost/libs/log/example/syslog/Jamfile.v2 new file mode 100644 index 00000000..a4d1a971 --- /dev/null +++ b/src/boost/libs/log/example/syslog/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <threading>multi + ; + +exe syslog + : main.cpp + ; diff --git a/src/boost/libs/log/example/syslog/main.cpp b/src/boost/libs/log/example/syslog/main.cpp new file mode 100644 index 00000000..18db0919 --- /dev/null +++ b/src/boost/libs/log/example/syslog/main.cpp @@ -0,0 +1,100 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 07.03.2009 + * + * \brief An example of logging to a syslog server (syslogd, for example). + * + * The example shows how to initialize logging to a remote syslog server. + * The code creates a sink that will send syslog messages to local port 514. + */ + +// #define BOOST_LOG_DYN_LINK 1 + +#include <boost/config.hpp> +#if !defined(BOOST_WINDOWS) +#define BOOST_LOG_USE_NATIVE_SYSLOG +#endif + +#include <stdexcept> +#include <string> +#include <iostream> +#include <boost/smart_ptr/shared_ptr.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks/sync_frontend.hpp> +#include <boost/log/sinks/syslog_backend.hpp> + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace sinks = boost::log::sinks; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +using boost::shared_ptr; + +//! Define application-specific severity levels +enum severity_levels +{ + normal, + warning, + error +}; + +int main(int argc, char* argv[]) +{ + try + { + // Create a syslog sink + shared_ptr< sinks::synchronous_sink< sinks::syslog_backend > > sink( + new sinks::synchronous_sink< sinks::syslog_backend >()); + + sink->set_formatter + ( + expr::format("syslog.exe: %1%: %2%") + % expr::attr< unsigned int >("RecordID") + % expr::smessage + ); + + // We'll have to map our custom levels to the syslog levels + sinks::syslog::custom_severity_mapping< severity_levels > mapping("Severity"); + mapping[normal] = sinks::syslog::info; + mapping[warning] = sinks::syslog::warning; + mapping[error] = sinks::syslog::critical; + + sink->locked_backend()->set_severity_mapper(mapping); + +#if !defined(BOOST_LOG_NO_ASIO) + // Set the remote address to sent syslog messages to + sink->locked_backend()->set_target_address("localhost"); +#endif + + // Add the sink to the core + logging::core::get()->add_sink(sink); + + // Add some attributes too + logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); + + // Do some logging + src::severity_logger< severity_levels > lg(keywords::severity = normal); + BOOST_LOG_SEV(lg, normal) << "A syslog record with normal level"; + BOOST_LOG_SEV(lg, warning) << "A syslog record with warning level"; + BOOST_LOG_SEV(lg, error) << "A syslog record with error level"; + + return 0; + } + catch (std::exception& e) + { + std::cout << "FAILURE: " << e.what() << std::endl; + return 1; + } +} diff --git a/src/boost/libs/log/example/trivial/Jamfile.v2 b/src/boost/libs/log/example/trivial/Jamfile.v2 new file mode 100644 index 00000000..3a02bf38 --- /dev/null +++ b/src/boost/libs/log/example/trivial/Jamfile.v2 @@ -0,0 +1,57 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/thread//boost_thread + ; + +exe trivial + : main.cpp + ; diff --git a/src/boost/libs/log/example/trivial/main.cpp b/src/boost/libs/log/example/trivial/main.cpp new file mode 100644 index 00000000..a59adb3b --- /dev/null +++ b/src/boost/libs/log/example/trivial/main.cpp @@ -0,0 +1,49 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 07.11.2009 + * + * \brief An example of trivial logging. + */ + +// #define BOOST_ALL_DYN_LINK 1 +// #define BOOST_LOG_DYN_LINK 1 + +#include <boost/log/trivial.hpp> +#include <boost/log/core.hpp> +#include <boost/log/expressions.hpp> + +int main(int argc, char* argv[]) +{ + // Trivial logging: all log records are written into a file + BOOST_LOG_TRIVIAL(trace) << "A trace severity message"; + BOOST_LOG_TRIVIAL(debug) << "A debug severity message"; + BOOST_LOG_TRIVIAL(info) << "An informational severity message"; + BOOST_LOG_TRIVIAL(warning) << "A warning severity message"; + BOOST_LOG_TRIVIAL(error) << "An error severity message"; + BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message"; + + // Filtering can also be applied + using namespace boost::log; + + core::get()->set_filter + ( + trivial::severity >= trivial::info + ); + + // Now the first two lines will not pass the filter + BOOST_LOG_TRIVIAL(trace) << "A trace severity message"; + BOOST_LOG_TRIVIAL(debug) << "A debug severity message"; + BOOST_LOG_TRIVIAL(info) << "An informational severity message"; + BOOST_LOG_TRIVIAL(warning) << "A warning severity message"; + BOOST_LOG_TRIVIAL(error) << "An error severity message"; + BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message"; + + return 0; +} diff --git a/src/boost/libs/log/example/wide_char/Jamfile.v2 b/src/boost/libs/log/example/wide_char/Jamfile.v2 new file mode 100644 index 00000000..7a3c518e --- /dev/null +++ b/src/boost/libs/log/example/wide_char/Jamfile.v2 @@ -0,0 +1,62 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +import ../../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <link>shared:<define>BOOST_ALL_DYN_LINK + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <library>/boost/log//boost_log + <library>/boost/log//boost_log_setup + <library>/boost/date_time//boost_date_time + <library>/boost/filesystem//boost_filesystem + <library>/boost/locale//boost_locale + <library>/boost/thread//boost_thread + <threading>multi + + # does not build on VxWorks due to lack of boost::locale support + <target-os>vxworks:<build>no + ; + +exe wide_char + : main.cpp + ; diff --git a/src/boost/libs/log/example/wide_char/main.cpp b/src/boost/libs/log/example/wide_char/main.cpp new file mode 100644 index 00000000..0c52afaa --- /dev/null +++ b/src/boost/libs/log/example/wide_char/main.cpp @@ -0,0 +1,121 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 main.cpp + * \author Andrey Semashev + * \date 01.12.2012 + * + * \brief An example of wide character logging. + */ + +#include <iostream> +#include <boost/locale/generator.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +#include <boost/log/common.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/utility/setup/file.hpp> +#include <boost/log/utility/setup/console.hpp> +#include <boost/log/utility/setup/common_attributes.hpp> +#include <boost/log/sources/logger.hpp> +#include <boost/log/support/date_time.hpp> + +namespace logging = boost::log; +namespace sinks = boost::log::sinks; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +//[ example_wide_char_severity_level_definition +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< ( + std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl) +{ + static const char* const str[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str))) + strm << str[lvl]; + else + strm << static_cast< int >(lvl); + return strm; +} +//] + +//[ example_wide_char_logging_initialization +// Declare attribute keywords +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) +BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime) + +void init_logging() +{ + boost::shared_ptr< sinks::synchronous_sink< sinks::text_file_backend > > sink = logging::add_file_log + ( + "sample.log", + keywords::format = expr::stream + << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") + << " <" << severity.or_default(normal) + << "> " << expr::message + ); + + // The sink will perform character code conversion as needed, according to the locale set with imbue() + std::locale loc = boost::locale::generator()("en_US.UTF-8"); + sink->imbue(loc); + + // Let's add some commonly used attributes, like timestamp and record counter. + logging::add_common_attributes(); +} +//] + +//[ example_wide_char_logging +void test_narrow_char_logging() +{ + // Narrow character logging still works + src::logger lg; + BOOST_LOG(lg) << "Hello, World! This is a narrow character message."; +} + +void test_wide_char_logging() +{ + src::wlogger lg; + BOOST_LOG(lg) << L"Hello, World! This is a wide character message."; + + // National characters are also supported + const wchar_t national_chars[] = { 0x041f, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, L',', L' ', 0x043c, 0x0438, 0x0440, L'!', 0 }; + BOOST_LOG(lg) << national_chars; + + // Now, let's try logging with severity + src::wseverity_logger< severity_level > slg; + BOOST_LOG_SEV(slg, normal) << L"A normal severity message, will not pass to the file"; + BOOST_LOG_SEV(slg, warning) << L"A warning severity message, will pass to the file"; + BOOST_LOG_SEV(slg, error) << L"An error severity message, will pass to the file"; +} +//] + +int main(int argc, char* argv[]) +{ + init_logging(); + test_narrow_char_logging(); + test_wide_char_logging(); + + return 0; +} diff --git a/src/boost/libs/log/index.html b/src/boost/libs/log/index.html new file mode 100644 index 00000000..3a9bf94c --- /dev/null +++ b/src/boost/libs/log/index.html @@ -0,0 +1,13 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; URL=doc/html/index.html"> +</head> +<body> +Automatic redirection failed, please go to +<a href="doc/html/index.html">doc/html/index.html</a> <hr> +<p>© Copyright Andrey Semashev, 2013</p> +<p>Distributed under the Boost Software License, Version 1.0. (See accompanying +file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy +at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p> +</body> +</html> diff --git a/src/boost/libs/log/meta/libraries.json b/src/boost/libs/log/meta/libraries.json new file mode 100644 index 00000000..08e82d14 --- /dev/null +++ b/src/boost/libs/log/meta/libraries.json @@ -0,0 +1,14 @@ +{ + "key": "log", + "name": "Log", + "authors": [ + "Andrey Semashev" + ], + "description": "Logging library.", + "category": [ + "Miscellaneous" + ], + "maintainers": [ + "Andrey Semashev <andrey.semashev -at- gmail.com>" + ] +} diff --git a/src/boost/libs/log/src/alignment_gap_between.hpp b/src/boost/libs/log/src/alignment_gap_between.hpp new file mode 100644 index 00000000..cc16a9f1 --- /dev/null +++ b/src/boost/libs/log/src/alignment_gap_between.hpp @@ -0,0 +1,50 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 alignment_gap_between.hpp + * \author Andrey Semashev + * \date 20.11.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_ALIGNMENT_GAP_BETWEEN_HPP_INCLUDED_ +#define BOOST_LOG_ALIGNMENT_GAP_BETWEEN_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/type_traits/alignment_of.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! The metafunction computes the minimal gap between objects t1 and t2 of types T1 and T2 +//! that would be needed to maintain the alignment of t2 if it's placed right after t1 +template< typename T1, typename T2 > +struct alignment_gap_between +{ + enum _ + { + T2_alignment = boost::alignment_of< T2 >::value, + tail_size = sizeof(T1) % T2_alignment, + value = tail_size > 0 ? T2_alignment - tail_size : 0 + }; +}; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_ALIGNMENT_GAP_BETWEEN_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/attribute_name.cpp b/src/boost/libs/log/src/attribute_name.cpp new file mode 100644 index 00000000..a804fed8 --- /dev/null +++ b/src/boost/libs/log/src/attribute_name.cpp @@ -0,0 +1,228 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attribute_name.cpp + * \author Andrey Semashev + * \date 28.06.2010 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstring> +#include <deque> +#include <ostream> +#include <stdexcept> +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/intrusive/set.hpp> +#include <boost/intrusive/set_hook.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/log/detail/locks.hpp> +#include <boost/log/detail/light_rw_mutex.hpp> +#endif +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +//! A global container of all known attribute names +class attribute_name::repository : + public log::aux::lazy_singleton< + repository, + shared_ptr< repository > + > +{ + typedef log::aux::lazy_singleton< + repository, + shared_ptr< repository > + > base_type; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) + friend class log::aux::lazy_singleton< + repository, + shared_ptr< repository > + >; +#else + friend class base_type; +#endif + +public: + // Import types from the basic_attribute_name template + typedef attribute_name::id_type id_type; + typedef attribute_name::string_type string_type; + + //! A base hook for arranging the attribute names into a set + typedef intrusive::set_base_hook< + intrusive::link_mode< intrusive::safe_link >, + intrusive::optimize_size< true > + > node_by_name_hook; + +private: + //! An element of the attribute names repository + struct node : + public node_by_name_hook + { + typedef node_by_name_hook base_type; + + public: + //! A predicate for name-based ordering + struct order_by_name + { + typedef bool result_type; + + bool operator() (node const& left, node const& right) const + { + return std::strcmp(left.m_name.c_str(), right.m_name.c_str()) < 0; + } + bool operator() (node const& left, const char* right) const + { + return std::strcmp(left.m_name.c_str(), right) < 0; + } + bool operator() (const char* left, node const& right) const + { + return std::strcmp(left, right.m_name.c_str()) < 0; + } + }; + + public: + id_type m_id; + string_type m_name; + + public: + node() : m_id(0), m_name() {} + node(id_type i, string_type const& n) : + base_type(), + m_id(i), + m_name(n) + { + } + node(node const& that) : + base_type(), + m_id(that.m_id), + m_name(that.m_name) + { + } + }; + + //! The container that provides storage for nodes + typedef std::deque< node > node_list; + //! The container that provides name-based lookup + typedef intrusive::set< + node, + intrusive::base_hook< node_by_name_hook >, + intrusive::constant_time_size< false >, + intrusive::compare< node::order_by_name > + > node_set; + +private: +#if !defined(BOOST_LOG_NO_THREADS) + typedef log::aux::light_rw_mutex mutex_type; + log::aux::light_rw_mutex m_Mutex; +#endif + node_list m_NodeList; + node_set m_NodeSet; + +public: + //! Converts attribute name string to id + id_type get_id_from_string(const char* name) + { + BOOST_ASSERT(name != NULL); + +#if !defined(BOOST_LOG_NO_THREADS) + { + // Do a non-blocking lookup first + log::aux::shared_lock_guard< mutex_type > _(m_Mutex); + node_set::const_iterator it = + m_NodeSet.find(name, node::order_by_name()); + if (it != m_NodeSet.end()) + return it->m_id; + } +#endif // !defined(BOOST_LOG_NO_THREADS) + + BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< mutex_type > _(m_Mutex);) + node_set::iterator it = + m_NodeSet.lower_bound(name, node::order_by_name()); + if (it == m_NodeSet.end() || it->m_name != name) + { + const std::size_t new_id = m_NodeList.size(); + if (new_id >= static_cast< id_type >(attribute_name::uninitialized)) + BOOST_THROW_EXCEPTION(limitation_error("Too many log attribute names")); + + m_NodeList.push_back(node(static_cast< id_type >(new_id), name)); + it = m_NodeSet.insert(it, m_NodeList.back()); + } + return it->m_id; + } + + //! Converts id to the attribute name string + string_type const& get_string_from_id(id_type id) + { + BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< mutex_type > _(m_Mutex);) + BOOST_ASSERT(id < m_NodeList.size()); + return m_NodeList[id].m_name; + } + +private: + //! Initializes the singleton instance + static void init_instance() + { + base_type::get_instance() = boost::make_shared< repository >(); + } +}; + +BOOST_LOG_API attribute_name::id_type +attribute_name::get_id_from_string(const char* name) +{ + return repository::get()->get_id_from_string(name); +} + +BOOST_LOG_API attribute_name::string_type const& +attribute_name::get_string_from_id(id_type id) +{ + return repository::get()->get_string_from_id(id); +} + +template< typename CharT, typename TraitsT > +BOOST_LOG_API std::basic_ostream< CharT, TraitsT >& operator<< ( + std::basic_ostream< CharT, TraitsT >& strm, + attribute_name const& name) +{ + if (!!name) + strm << name.string().c_str(); + else + strm << "[uninitialized]"; + return strm; +} + +// Explicitly instantiate attribute name implementation +#ifdef BOOST_LOG_USE_CHAR +template BOOST_LOG_API std::basic_ostream< char, std::char_traits< char > >& + operator<< < char, std::char_traits< char > >( + std::basic_ostream< char, std::char_traits< char > >& strm, + attribute_name const& name); +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_API std::basic_ostream< wchar_t, std::char_traits< wchar_t > >& + operator<< < wchar_t, std::char_traits< wchar_t > >( + std::basic_ostream< wchar_t, std::char_traits< wchar_t > >& strm, + attribute_name const& name); +#endif + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/attribute_set.cpp b/src/boost/libs/log/src/attribute_set.cpp new file mode 100644 index 00000000..27379e13 --- /dev/null +++ b/src/boost/libs/log/src/attribute_set.cpp @@ -0,0 +1,148 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attribute_set.cpp + * \author Andrey Semashev + * \date 19.04.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <deque> +#include <boost/assert.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/link_mode.hpp> +#include <boost/intrusive/derivation_value_traits.hpp> +#include <boost/log/attributes/attribute.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include "attribute_set_impl.hpp" +#include "stateless_allocator.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_LOG_API void* attribute::impl::operator new (std::size_t size) +{ + return aux::stateless_allocator< unsigned char >().allocate(size); +} + +BOOST_LOG_API void attribute::impl::operator delete (void* p, std::size_t size) BOOST_NOEXCEPT +{ + aux::stateless_allocator< unsigned char >().deallocate(static_cast< unsigned char* >(p), size); +} + +inline attribute_set::node_base::node_base() : + m_pPrev(NULL), + m_pNext(NULL) +{ +} + +inline attribute_set::node::node(key_type const& key, mapped_type const& data) : + node_base(), + m_Value(key, data) +{ +} + +//! Default constructor +BOOST_LOG_API attribute_set::attribute_set() : + m_pImpl(new implementation()) +{ +} + +//! Copy constructor +BOOST_LOG_API attribute_set::attribute_set(attribute_set const& that) : + m_pImpl(new implementation(*that.m_pImpl)) +{ +} + +//! Destructor +BOOST_LOG_API attribute_set::~attribute_set() BOOST_NOEXCEPT +{ + delete m_pImpl; +} + +// Iterator generators +BOOST_LOG_API attribute_set::iterator attribute_set::begin() BOOST_NOEXCEPT +{ + return m_pImpl->begin(); +} +BOOST_LOG_API attribute_set::iterator attribute_set::end() BOOST_NOEXCEPT +{ + return m_pImpl->end(); +} +BOOST_LOG_API attribute_set::const_iterator attribute_set::begin() const BOOST_NOEXCEPT +{ + return const_iterator(m_pImpl->begin()); +} +BOOST_LOG_API attribute_set::const_iterator attribute_set::end() const BOOST_NOEXCEPT +{ + return const_iterator(m_pImpl->end()); +} + +//! The method returns number of elements in the container +BOOST_LOG_API attribute_set::size_type attribute_set::size() const BOOST_NOEXCEPT +{ + return m_pImpl->size(); +} + +//! Insertion method +BOOST_LOG_API std::pair< attribute_set::iterator, bool > +attribute_set::insert(key_type key, mapped_type const& data) +{ + return m_pImpl->insert(key, data); +} + +//! The method erases all attributes with the specified name +BOOST_LOG_API attribute_set::size_type attribute_set::erase(key_type key) BOOST_NOEXCEPT +{ + iterator it = m_pImpl->find(key); + if (it != end()) + { + m_pImpl->erase(it); + return 1; + } + else + return 0; +} + +//! The method erases the specified attribute +BOOST_LOG_API void attribute_set::erase(iterator it) BOOST_NOEXCEPT +{ + m_pImpl->erase(it); +} + +//! The method erases all attributes within the specified range +BOOST_LOG_API void attribute_set::erase(iterator begin, iterator end) BOOST_NOEXCEPT +{ + while (begin != end) + { + m_pImpl->erase(begin++); + } +} + +//! The method clears the container +BOOST_LOG_API void attribute_set::clear() BOOST_NOEXCEPT +{ + m_pImpl->clear(); +} + +//! Internal lookup implementation +BOOST_LOG_API attribute_set::iterator attribute_set::find(key_type key) BOOST_NOEXCEPT +{ + return m_pImpl->find(key); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/attribute_set_impl.hpp b/src/boost/libs/log/src/attribute_set_impl.hpp new file mode 100644 index 00000000..e9de28ec --- /dev/null +++ b/src/boost/libs/log/src/attribute_set_impl.hpp @@ -0,0 +1,389 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attribute_set_impl.hpp + * \author Andrey Semashev + * \date 03.07.2010 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ +#define BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <new> +#include <memory> +#include <limits> +#include <utility> +#include <algorithm> +#include <cstddef> +#include <boost/assert.hpp> +#include <boost/array.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/link_mode.hpp> +#include <boost/intrusive/derivation_value_traits.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/detail/header.hpp> + +#ifndef BOOST_LOG_HASH_TABLE_SIZE_LOG +// Hash table size will be 2 ^ this value +#define BOOST_LOG_HASH_TABLE_SIZE_LOG 4 +#endif + +#ifndef BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE +// Maximum pool size that each attribute set maintains +#define BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE 8 +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +//! A simple pooling allocator +template< typename T > +class pool_allocator : + public std::allocator< T > +{ +public: + template< typename U > + struct rebind + { + typedef pool_allocator< U > other; + }; + + typedef std::allocator< T > base_type; + +#if BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > 0 + + typedef typename base_type::value_type value_type; + typedef typename base_type::size_type size_type; + typedef typename base_type::difference_type difference_type; + typedef typename base_type::pointer pointer; + typedef typename base_type::const_pointer const_pointer; + typedef typename base_type::reference reference; + typedef typename base_type::const_reference const_reference; + +private: + array< pointer, BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > m_Pool; + size_type m_PooledCount; + +public: + pool_allocator() : m_PooledCount(0) + { + } + + pool_allocator(pool_allocator const& that) : + base_type(static_cast< base_type const& >(that)), + m_PooledCount(0) + { + } + + template< typename U > + pool_allocator(pool_allocator< U > const& that) : + base_type(static_cast< typename pool_allocator< U >::base_type const& >(that)), + m_PooledCount(0) + { + } + + ~pool_allocator() + { + for (size_type i = 0; i < m_PooledCount; ++i) + { + base_type::deallocate(m_Pool[i], 1); + } + } + + pool_allocator& operator= (pool_allocator const& that) + { + base_type::operator= (static_cast< base_type const& >(that)); + return *this; + } + + template< typename U > + pool_allocator& operator= (pool_allocator< U > const& that) + { + base_type::operator= ( + static_cast< typename pool_allocator< U >::base_type const& >(that)); + return *this; + } + + pointer allocate(size_type n, const void* hint = NULL) + { + if (m_PooledCount > 0) + { + --m_PooledCount; + return m_Pool[m_PooledCount]; + } + else + return base_type::allocate(n, hint); + } + + void deallocate(pointer p, size_type n) + { + if (m_PooledCount < m_Pool.size()) + { + m_Pool[m_PooledCount] = p; + ++m_PooledCount; + } + else + base_type::deallocate(p, n); + } + +#else + + template< typename U > + pool_allocator(pool_allocator< U > const& that) : + base_type(static_cast< typename pool_allocator< U >::base_type const& >(that)) + { + } + +#endif // BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > 0 +}; + +//! Attribute set implementation +struct attribute_set::implementation +{ +public: + //! Attribute name identifier type + typedef key_type::id_type id_type; + + //! Allocator type + typedef pool_allocator< node > node_allocator; + + //! Node base class traits for the intrusive list + struct node_traits + { + typedef node_base node; + typedef node* node_ptr; + typedef node const* const_node_ptr; + static node* get_next(const node* n) { return n->m_pNext; } + static void set_next(node* n, node* next) { n->m_pNext = next; } + static node* get_previous(const node* n) { return n->m_pPrev; } + static void set_previous(node* n, node* prev) { n->m_pPrev = prev; } + }; + + //! Contained node traits for the intrusive list + typedef intrusive::derivation_value_traits< + node, + node_traits, + intrusive::normal_link + > value_traits; + + //! The container that allows to iterate through elements + typedef intrusive::list< + node, + intrusive::value_traits< value_traits >, + intrusive::constant_time_size< true > + > node_list; + + //! A hash table bucket + struct bucket + { + //! Points to the first element in the bucket + node* first; + //! Points to the last element in the bucket (not the one after the last!) + node* last; + + bucket() : first(NULL), last(NULL) {} + }; + + //! A list of buckets + typedef boost::array< bucket, 1U << BOOST_LOG_HASH_TABLE_SIZE_LOG > buckets; + + //! Cleanup function object used to erase elements from the container + struct disposer + { + typedef void result_type; + + explicit disposer(node_allocator& alloc) : m_Allocator(alloc) + { + } + void operator() (node* p) const + { + p->~node(); + m_Allocator.deallocate(p, 1); + } + + private: + node_allocator& m_Allocator; + }; + +private: + //! List of nodes + node_list m_Nodes; + //! Node allocator + node_allocator m_Allocator; + //! Hash table buckets + buckets m_Buckets; + +public: + implementation() + { + } + + implementation(implementation const& that) : m_Allocator(that.m_Allocator) + { + node_list::const_iterator it = that.m_Nodes.begin(), end = that.m_Nodes.end(); + for (; it != end; ++it) + { + node* const n = m_Allocator.allocate(1, NULL); + new (n) node(it->m_Value.first, it->m_Value.second); + m_Nodes.push_back(*n); + + bucket& b = get_bucket(it->m_Value.first.id()); + if (b.first == NULL) + b.first = b.last = n; + else + b.last = n; + } + } + + ~implementation() + { + m_Nodes.clear_and_dispose(disposer(m_Allocator)); + } + + size_type size() const { return m_Nodes.size(); } + iterator begin() { return iterator(m_Nodes.begin().pointed_node()); } + iterator end() { return iterator(m_Nodes.end().pointed_node()); } + + void clear() + { + m_Nodes.clear_and_dispose(disposer(m_Allocator)); + std::fill_n(m_Buckets.begin(), m_Buckets.size(), bucket()); + } + + std::pair< iterator, bool > insert(key_type key, mapped_type const& data) + { + BOOST_ASSERT(!!key); + + bucket& b = get_bucket(key.id()); + node* p = b.first; + if (p) + { + // The bucket is not empty, search among the elements + p = find_in_bucket(key, b); + if (p->m_Value.first == key) + return std::make_pair(iterator(p), false); + } + + node* const n = m_Allocator.allocate(1, NULL); + new (n) node(key, data); + + node_list::iterator it; + if (b.first == NULL) + { + // The bucket is empty + b.first = b.last = n; + it = m_Nodes.end(); + } + else if (p == b.last && key.id() > p->m_Value.first.id()) + { + // The new element should become the last element of the bucket + it = m_Nodes.iterator_to(*p); + ++it; + b.last = n; + } + else if (p == b.first) + { + // The new element should become the first element of the bucket + it = m_Nodes.iterator_to(*p); + b.first = n; + } + else + { + // The new element should be within the bucket + it = m_Nodes.iterator_to(*p); + } + + m_Nodes.insert(it, *n); + + return std::make_pair(iterator(n), true); + } + + void erase(iterator it) + { + typedef node_list::node_traits node_traits; + typedef node_list::value_traits value_traits; + + node* p = static_cast< node* >(it.base()); + + // Adjust bucket boundaries, if needed + bucket& b = get_bucket(it->first.id()); + if (p == b.first) + { + if (p == b.last) + { + // The erased element is the only one in the bucket + b.first = b.last = NULL; + } + else + { + // The erased element is the first one in the bucket + b.first = value_traits::to_value_ptr(node_traits::get_next(b.first)); + } + } + else if (p == b.last) + { + // The erased element is the last one in the bucket + b.last = value_traits::to_value_ptr(node_traits::get_previous(b.last)); + } + + m_Nodes.erase_and_dispose(m_Nodes.iterator_to(*p), disposer(m_Allocator)); + } + + iterator find(key_type key) + { + bucket& b = get_bucket(key.id()); + node* p = b.first; + if (p) + { + // The bucket is not empty, search among the elements + p = find_in_bucket(key, b); + if (p->m_Value.first == key) + return iterator(p); + } + + return end(); + } + +private: + implementation& operator= (implementation const&); + + //! The function returns a bucket for the specified element + bucket& get_bucket(id_type id) + { + return m_Buckets[id & (buckets::static_size - 1)]; + } + + //! Attempts to find an element with the specified key in the bucket + node* find_in_bucket(key_type key, bucket const& b) + { + typedef node_list::node_traits node_traits; + typedef node_list::value_traits value_traits; + + // All elements within the bucket are sorted to speedup the search. + node* p = b.first; + while (p != b.last && p->m_Value.first.id() < key.id()) + { + p = value_traits::to_value_ptr(node_traits::get_next(p)); + } + + return p; + } +}; + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/attribute_value_set.cpp b/src/boost/libs/log/src/attribute_value_set.cpp new file mode 100644 index 00000000..14d9752a --- /dev/null +++ b/src/boost/libs/log/src/attribute_value_set.cpp @@ -0,0 +1,570 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attribute_value_set.cpp + * \author Andrey Semashev + * \date 19.04.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <new> +#include <memory> +#include <boost/array.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/link_mode.hpp> +#include <boost/intrusive/derivation_value_traits.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/attributes/attribute_value.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include "alignment_gap_between.hpp" +#include "attribute_set_impl.hpp" +#include "stateless_allocator.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_FORCEINLINE attribute_value_set::node_base::node_base() : + m_pPrev(NULL), + m_pNext(NULL) +{ +} + +BOOST_FORCEINLINE attribute_value_set::node::node(key_type const& key, mapped_type& data, bool dynamic) : + node_base(), + m_Value(key, mapped_type()), + m_DynamicallyAllocated(dynamic) +{ + m_Value.second.swap(data); +} + +//! Container implementation +struct attribute_value_set::implementation +{ +public: + typedef key_type::id_type id_type; + +private: + typedef attribute_set::implementation attribute_set_impl_type; + typedef boost::log::aux::stateless_allocator< char > stateless_allocator; + + //! Node base class traits for the intrusive list + struct node_traits + { + typedef node_base node; + typedef node* node_ptr; + typedef node const* const_node_ptr; + static node* get_next(const node* n) { return n->m_pNext; } + static void set_next(node* n, node* next) { n->m_pNext = next; } + static node* get_previous(const node* n) { return n->m_pPrev; } + static void set_previous(node* n, node* prev) { n->m_pPrev = prev; } + }; + + //! Contained node traits for the intrusive list + typedef intrusive::derivation_value_traits< + node, + node_traits, + intrusive::normal_link + > value_traits; + + //! A container that provides iteration through elements of the container + typedef intrusive::list< + node, + intrusive::value_traits< value_traits >, + intrusive::constant_time_size< true > + > node_list; + + //! A hash table bucket + struct bucket + { + //! Points to the first element in the bucket + node* first; + //! Points to the last element in the bucket (not the one after the last!) + node* last; + + bucket() : first(NULL), last(NULL) {} + }; + + //! A list of buckets + typedef boost::array< bucket, 1u << BOOST_LOG_HASH_TABLE_SIZE_LOG > buckets; + + //! Element disposer + struct disposer + { + typedef void result_type; + void operator() (node* p) const BOOST_NOEXCEPT + { + if (!p->m_DynamicallyAllocated) + p->~node(); + else + delete p; + } + }; + +private: + //! Pointer to the source-specific attributes + attribute_set_impl_type* m_pSourceAttributes; + //! Pointer to the thread-specific attributes + attribute_set_impl_type* m_pThreadAttributes; + //! Pointer to the global attributes + attribute_set_impl_type* m_pGlobalAttributes; + + //! The container with elements + node_list m_Nodes; + //! The pointer to the end of the allocated elements within the storage + node* m_pEnd; + //! The pointer to the end of storage + node* m_pEOS; + + //! Hash table buckets + buckets m_Buckets; + +private: + //! Constructor + implementation( + node* storage, + node* eos, + attribute_set_impl_type* source_attrs, + attribute_set_impl_type* thread_attrs, + attribute_set_impl_type* global_attrs + ) : + m_pSourceAttributes(source_attrs), + m_pThreadAttributes(thread_attrs), + m_pGlobalAttributes(global_attrs), + m_pEnd(storage), + m_pEOS(eos) + { + } + + //! Destructor + ~implementation() + { + m_Nodes.clear_and_dispose(disposer()); + } + + //! The function allocates memory and creates the object + static implementation* create( + size_type element_count, + attribute_set_impl_type* source_attrs, + attribute_set_impl_type* thread_attrs, + attribute_set_impl_type* global_attrs) + { + // Calculate the buffer size + const size_type header_size = sizeof(implementation) + + aux::alignment_gap_between< implementation, node >::value; + const size_type buffer_size = header_size + element_count * sizeof(node); + + implementation* p = reinterpret_cast< implementation* >(stateless_allocator().allocate(buffer_size)); + node* const storage = reinterpret_cast< node* >(reinterpret_cast< char* >(p) + header_size); + new (p) implementation(storage, storage + element_count, source_attrs, thread_attrs, global_attrs); + + return p; + } + +public: + //! The function allocates memory and creates the object + static implementation* create( + attribute_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count) + { + return create( + source_attrs.m_pImpl->size() + thread_attrs.m_pImpl->size() + global_attrs.m_pImpl->size() + reserve_count, + source_attrs.m_pImpl, + thread_attrs.m_pImpl, + global_attrs.m_pImpl); + } + + //! The function allocates memory and creates the object + static implementation* create( + attribute_value_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count) + { + implementation* p = create( + source_attrs.m_pImpl->size() + thread_attrs.m_pImpl->size() + global_attrs.m_pImpl->size() + reserve_count, + NULL, + thread_attrs.m_pImpl, + global_attrs.m_pImpl); + p->copy_nodes_from(source_attrs.m_pImpl); + return p; + } + + //! The function allocates memory and creates the object + static implementation* create( + BOOST_RV_REF(attribute_value_set) source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count) + { + implementation* p = source_attrs.m_pImpl; + source_attrs.m_pImpl = NULL; + p->m_pThreadAttributes = thread_attrs.m_pImpl; + p->m_pGlobalAttributes = global_attrs.m_pImpl; + return p; + } + + //! The function allocates memory and creates the object + static implementation* create(size_type reserve_count) + { + return create(reserve_count, NULL, NULL, NULL); + } + + //! Creates a copy of the object + static implementation* copy(implementation* that) + { + // Create new object + implementation* p = create(that->size(), NULL, NULL, NULL); + + // Copy all elements + p->copy_nodes_from(that); + + return p; + } + + //! Destroys the object and releases the memory + static void destroy(implementation* p) + { + const size_type buffer_size = reinterpret_cast< char* >(p->m_pEOS) - reinterpret_cast< char* >(p); + p->~implementation(); + stateless_allocator().deallocate(reinterpret_cast< stateless_allocator::pointer >(p), buffer_size); + } + + //! Returns the pointer to the first element + node_base* begin() + { + freeze(); + return m_Nodes.begin().pointed_node(); + } + //! Returns the pointer after the last element + node_base* end() + { + return m_Nodes.end().pointed_node(); + } + + //! Returns the number of elements in the container + size_type size() + { + freeze(); + return m_Nodes.size(); + } + + //! Looks for the element with an equivalent key + node_base* find(key_type key) + { + // First try to find an acquired element + bucket& b = get_bucket(key.id()); + node* p = b.first; + if (p) + { + // The bucket is not empty, search among the elements + p = find_in_bucket(key, b); + if (p->m_Value.first == key) + return p; + } + + // Element not found, try to acquire the value from attribute sets + return freeze_node(key, b, p); + } + + //! Freezes all elements of the container + void freeze() + { + if (m_pSourceAttributes) + { + freeze_nodes_from(m_pSourceAttributes); + m_pSourceAttributes = NULL; + } + if (m_pThreadAttributes) + { + freeze_nodes_from(m_pThreadAttributes); + m_pThreadAttributes = NULL; + } + if (m_pGlobalAttributes) + { + freeze_nodes_from(m_pGlobalAttributes); + m_pGlobalAttributes = NULL; + } + } + + //! Inserts an element + std::pair< node*, bool > insert(key_type key, mapped_type const& mapped) + { + bucket& b = get_bucket(key.id()); + node* p = find_in_bucket(key, b); + if (!p || p->m_Value.first != key) + { + p = insert_node(key, b, p, mapped); + return std::pair< node*, bool >(p, true); + } + else + { + return std::pair< node*, bool >(p, false); + } + } + +private: + //! The function returns a bucket for the specified element + bucket& get_bucket(id_type id) + { + return m_Buckets[id & (buckets::static_size - 1u)]; + } + + //! Attempts to find an element with the specified key in the bucket + node* find_in_bucket(key_type key, bucket const& b) + { + typedef node_list::node_traits node_traits; + typedef node_list::value_traits value_traits; + + // All elements within the bucket are sorted to speedup the search. + node* p = b.first; + while (p != b.last && p->m_Value.first.id() < key.id()) + { + p = value_traits::to_value_ptr(node_traits::get_next(p)); + } + + return p; + } + + //! Acquires the attribute value from the attribute sets + node_base* freeze_node(key_type key, bucket& b, node* where) + { + attribute_set::iterator it; + if (m_pSourceAttributes) + { + it = m_pSourceAttributes->find(key); + if (it != m_pSourceAttributes->end()) + { + // The attribute is found, acquiring the value + return insert_node(key, b, where, it->second.get_value()); + } + } + + if (m_pThreadAttributes) + { + it = m_pThreadAttributes->find(key); + if (it != m_pThreadAttributes->end()) + { + // The attribute is found, acquiring the value + return insert_node(key, b, where, it->second.get_value()); + } + } + + if (m_pGlobalAttributes) + { + it = m_pGlobalAttributes->find(key); + if (it != m_pGlobalAttributes->end()) + { + // The attribute is found, acquiring the value + return insert_node(key, b, where, it->second.get_value()); + } + } + + // The attribute is not found + return m_Nodes.end().pointed_node(); + } + + //! The function inserts a node into the container + node* insert_node(key_type key, bucket& b, node* where, mapped_type data) + { + node* p; + if (m_pEnd != m_pEOS) + { + p = m_pEnd++; + new (p) node(key, data, false); + } + else + { + p = new node(key, data, true); + } + + node_list::iterator it; + if (b.first == NULL) + { + // The bucket is empty + b.first = b.last = p; + it = m_Nodes.end(); + } + else if (where == b.last && key.id() > where->m_Value.first.id()) + { + // The new element should become the last element of the bucket + it = m_Nodes.iterator_to(*where); + ++it; + b.last = p; + } + else if (where == b.first) + { + // The new element should become the first element of the bucket + it = m_Nodes.iterator_to(*where); + b.first = p; + } + else + { + // The new element should be within the bucket + it = m_Nodes.iterator_to(*where); + } + + m_Nodes.insert(it, *p); + + return p; + } + + //! Acquires attribute values from the set of attributes + void freeze_nodes_from(attribute_set_impl_type* attrs) + { + attribute_set::const_iterator it = attrs->begin(), end = attrs->end(); + for (; it != end; ++it) + { + key_type key = it->first; + bucket& b = get_bucket(key.id()); + node* p = b.first; + if (p) + { + p = find_in_bucket(key, b); + if (p->m_Value.first == key) + continue; // the element is already frozen + } + + insert_node(key, b, p, it->second.get_value()); + } + } + + //! Copies nodes of the container + void copy_nodes_from(implementation* from) + { + // Copy all elements + node_list::iterator it = from->m_Nodes.begin(), end = from->m_Nodes.end(); + for (; it != end; ++it) + { + node* n = m_pEnd++; + mapped_type data = it->m_Value.second; + new (n) node(it->m_Value.first, data, false); + m_Nodes.push_back(*n); + + // Since nodes within buckets are ordered, we can simply append the node to the end of the bucket + bucket& b = get_bucket(n->m_Value.first.id()); + if (b.first == NULL) + b.first = b.last = n; + else + b.last = n; + } + } +}; + +//! The constructor creates an empty set +BOOST_LOG_API attribute_value_set::attribute_value_set( + size_type reserve_count +) : + m_pImpl(implementation::create(reserve_count)) +{ +} + +//! The constructor adopts three attribute sets to the set +BOOST_LOG_API attribute_value_set::attribute_value_set( + attribute_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count +) : + m_pImpl(implementation::create(source_attrs, thread_attrs, global_attrs, reserve_count)) +{ +} + +//! The constructor adopts three attribute sets to the set +BOOST_LOG_API attribute_value_set::attribute_value_set( + attribute_value_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count +) : + m_pImpl(implementation::create(source_attrs, thread_attrs, global_attrs, reserve_count)) +{ +} + +//! The constructor adopts three attribute sets to the set +BOOST_LOG_API void attribute_value_set::construct( + attribute_value_set& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count +) +{ + m_pImpl = implementation::create(boost::move(source_attrs), thread_attrs, global_attrs, reserve_count); +} + +//! Copy constructor +BOOST_LOG_API attribute_value_set::attribute_value_set(attribute_value_set const& that) +{ + if (that.m_pImpl) + m_pImpl = implementation::copy(that.m_pImpl); + else + m_pImpl = NULL; +} + +//! Destructor +BOOST_LOG_API attribute_value_set::~attribute_value_set() BOOST_NOEXCEPT +{ + if (m_pImpl) + { + implementation::destroy(m_pImpl); + m_pImpl = NULL; + } +} + +// Iterator generators +BOOST_LOG_API attribute_value_set::const_iterator +attribute_value_set::begin() const +{ + return const_iterator(m_pImpl->begin(), const_cast< attribute_value_set* >(this)); +} + +BOOST_LOG_API attribute_value_set::const_iterator +attribute_value_set::end() const +{ + return const_iterator(m_pImpl->end(), const_cast< attribute_value_set* >(this)); +} + +//! The method returns number of elements in the container +BOOST_LOG_API attribute_value_set::size_type +attribute_value_set::size() const +{ + return m_pImpl->size(); +} + +//! Internal lookup implementation +BOOST_LOG_API attribute_value_set::const_iterator +attribute_value_set::find(key_type key) const +{ + return const_iterator(m_pImpl->find(key), const_cast< attribute_value_set* >(this)); +} + +//! The method acquires values of all adopted attributes. Users don't need to call it, since will always get an already frozen set. +BOOST_LOG_API void attribute_value_set::freeze() +{ + m_pImpl->freeze(); +} + +//! Inserts an element into the set +BOOST_LOG_API std::pair< attribute_value_set::const_iterator, bool > +attribute_value_set::insert(key_type key, mapped_type const& mapped) +{ + std::pair< node*, bool > res = m_pImpl->insert(key, mapped); + return std::pair< const_iterator, bool >(const_iterator(res.first, this), res.second); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/bit_tools.hpp b/src/boost/libs/log/src/bit_tools.hpp new file mode 100644 index 00000000..db28c405 --- /dev/null +++ b/src/boost/libs/log/src/bit_tools.hpp @@ -0,0 +1,51 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 bit_tools.hpp + * \author Andrey Semashev + * \date 16.01.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_BIT_TOOLS_HPP_INCLUDED_ +#define BOOST_LOG_BIT_TOOLS_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <boost/cstdint.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Checks that the integer is a power of 2. +template< typename T > +inline BOOST_CONSTEXPR bool is_power_of_2(T n) BOOST_NOEXCEPT +{ + return n != (T)0 && (n & (n - (T)1)) == (T)0; +} + +//! Returns an integer comprising the four characters +inline BOOST_CONSTEXPR uint32_t make_fourcc(unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4) BOOST_NOEXCEPT +{ + return (static_cast< uint32_t >(c1) << 24) | (static_cast< uint32_t >(c2) << 16) | (static_cast< uint32_t >(c3) << 8) | static_cast< uint32_t >(c4); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_BIT_TOOLS_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/code_conversion.cpp b/src/boost/libs/log/src/code_conversion.cpp new file mode 100644 index 00000000..2f253eda --- /dev/null +++ b/src/boost/libs/log/src/code_conversion.cpp @@ -0,0 +1,298 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 code_conversion.cpp + * \author Andrey Semashev + * \date 08.11.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <locale> +#include <string> +#include <stdexcept> +#include <algorithm> +#include <boost/log/exceptions.hpp> +#include <boost/log/detail/code_conversion.hpp> +#if defined(BOOST_WINDOWS) +#include <cstring> +#include <limits> +#include <boost/winapi/get_last_error.hpp> +#include <boost/winapi/character_code_conversion.hpp> +#endif +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! The function performs character conversion with the specified facet +template< typename LocalCharT > +inline std::codecvt_base::result convert( + std::codecvt< LocalCharT, char, std::mbstate_t > const& fac, + std::mbstate_t& state, + const char*& pSrcBegin, + const char* pSrcEnd, + LocalCharT*& pDstBegin, + LocalCharT* pDstEnd) +{ + return fac.in(state, pSrcBegin, pSrcEnd, pSrcBegin, pDstBegin, pDstEnd, pDstBegin); +} + +//! The function performs character conversion with the specified facet +template< typename LocalCharT > +inline std::codecvt_base::result convert( + std::codecvt< LocalCharT, char, std::mbstate_t > const& fac, + std::mbstate_t& state, + const LocalCharT*& pSrcBegin, + const LocalCharT* pSrcEnd, + char*& pDstBegin, + char* pDstEnd) +{ + return fac.out(state, pSrcBegin, pSrcEnd, pSrcBegin, pDstBegin, pDstEnd, pDstBegin); +} + +} // namespace + +template< typename SourceCharT, typename TargetCharT, typename FacetT > +inline std::size_t code_convert(const SourceCharT* begin, const SourceCharT* end, std::basic_string< TargetCharT >& converted, std::size_t max_size, FacetT const& fac) +{ + typedef typename FacetT::state_type state_type; + TargetCharT converted_buffer[256]; + + const SourceCharT* const original_begin = begin; + state_type state = state_type(); + std::size_t buf_size = (std::min)(max_size, sizeof(converted_buffer) / sizeof(*converted_buffer)); + while (begin != end && buf_size > 0u) + { + TargetCharT* dest = converted_buffer; + std::codecvt_base::result res = convert( + fac, + state, + begin, + end, + dest, + dest + buf_size); + + switch (res) + { + case std::codecvt_base::ok: + // All characters were successfully converted + // NOTE: MSVC 11 also returns ok when the source buffer was only partially consumed, so we also check that the begin pointer has reached the end. + converted.append(converted_buffer, dest); + max_size -= dest - converted_buffer; + break; + + case std::codecvt_base::noconv: + { + // Not possible, unless both character types are actually equivalent + const std::size_t size = (std::min)(max_size, static_cast< std::size_t >(end - begin)); + converted.append(begin, begin + size); + begin += size; + max_size -= size; + } + goto done; + + case std::codecvt_base::partial: + // Some characters were converted, some were not + if (dest != converted_buffer) + { + // Some conversion took place, so it seems like + // the destination buffer might not have been long enough + converted.append(converted_buffer, dest); + max_size -= dest - converted_buffer; + + // ...and go on for the next part + break; + } + else + { + // Nothing was converted + if (begin == end) + goto done; + + // Looks like the tail of the source buffer contains only part of the last character. + // In this case we intentionally fall through to throw an exception. + } + + default: // std::codecvt_base::error + BOOST_LOG_THROW_DESCR(conversion_error, "Could not convert character encoding"); + } + + buf_size = (std::min)(max_size, sizeof(converted_buffer) / sizeof(*converted_buffer)); + } + +done: + return static_cast< std::size_t >(begin - original_begin); +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const wchar_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) +{ + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == len; +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc) +{ + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == len; +} + +#if !defined(BOOST_LOG_NO_CXX11_CODECVT_FACETS) + +#if !defined(BOOST_NO_CXX11_CHAR16_T) + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) +{ + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)) == len; +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::u16string& str2, std::size_t max_size, std::locale const& loc) +{ + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)) == len; +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc) +{ + std::string temp_str; + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == temp_size; +} + +#endif + +#if !defined(BOOST_NO_CXX11_CHAR32_T) + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) +{ + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)) == len; +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char* str1, std::size_t len, std::u32string& str2, std::size_t max_size, std::locale const& loc) +{ + return code_convert(str1, str1 + len, str2, max_size, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)) == len; +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::wstring& str2, std::size_t max_size, std::locale const& loc) +{ + std::string temp_str; + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc)) == temp_size; +} + +#endif + +#if !defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CXX11_CHAR32_T) + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char16_t* str1, std::size_t len, std::u32string& str2, std::size_t max_size, std::locale const& loc) +{ + std::string temp_str; + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)) == temp_size; +} + +//! The function converts one string to the character type of another +BOOST_LOG_API bool code_convert_impl(const char32_t* str1, std::size_t len, std::u16string& str2, std::size_t max_size, std::locale const& loc) +{ + std::string temp_str; + code_convert(str1, str1 + len, temp_str, temp_str.max_size(), std::use_facet< std::codecvt< char32_t, char, std::mbstate_t > >(loc)); + const std::size_t temp_size = temp_str.size(); + return code_convert(temp_str.c_str(), temp_str.c_str() + temp_size, str2, max_size, std::use_facet< std::codecvt< char16_t, char, std::mbstate_t > >(loc)) == temp_size; +} + +#endif + +#endif // !defined(BOOST_LOG_NO_CXX11_CODECVT_FACETS) + +#if defined(BOOST_WINDOWS) + +//! Converts UTF-8 to UTF-16 +std::wstring utf8_to_utf16(const char* str) +{ + std::size_t utf8_len = std::strlen(str); + if (utf8_len == 0) + return std::wstring(); + else if (BOOST_UNLIKELY(utf8_len > static_cast< std::size_t >((std::numeric_limits< int >::max)()))) + BOOST_LOG_THROW_DESCR(bad_alloc, "UTF-8 string too long"); + + int len = boost::winapi::MultiByteToWideChar(boost::winapi::CP_UTF8_, boost::winapi::MB_ERR_INVALID_CHARS_, str, static_cast< int >(utf8_len), NULL, 0); + if (BOOST_LIKELY(len > 0)) + { + std::wstring wstr; + wstr.resize(len); + + len = boost::winapi::MultiByteToWideChar(boost::winapi::CP_UTF8_, boost::winapi::MB_ERR_INVALID_CHARS_, str, static_cast< int >(utf8_len), &wstr[0], len); + if (BOOST_LIKELY(len > 0)) + { + return wstr; + } + } + + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to convert UTF-8 to UTF-16", (err)); + BOOST_LOG_UNREACHABLE_RETURN(std::wstring()); +} + +//! Converts UTF-16 to UTF-8 +std::string utf16_to_utf8(const wchar_t* wstr) +{ + std::size_t utf16_len = std::wcslen(wstr); + if (utf16_len == 0) + return std::string(); + else if (BOOST_UNLIKELY(utf16_len > static_cast< std::size_t >((std::numeric_limits< int >::max)()))) + BOOST_LOG_THROW_DESCR(bad_alloc, "UTF-16 string too long"); + + const boost::winapi::DWORD_ flags = +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + boost::winapi::WC_ERR_INVALID_CHARS_; +#else + 0u; +#endif + int len = boost::winapi::WideCharToMultiByte(boost::winapi::CP_UTF8_, flags, wstr, static_cast< int >(utf16_len), NULL, 0, NULL, NULL); + if (BOOST_LIKELY(len > 0)) + { + std::string str; + str.resize(len); + + len = boost::winapi::WideCharToMultiByte(boost::winapi::CP_UTF8_, flags, wstr, static_cast< int >(utf16_len), &str[0], len, NULL, NULL); + if (BOOST_LIKELY(len > 0)) + { + return str; + } + } + + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to convert UTF-16 to UTF-8", (err)); + BOOST_LOG_UNREACHABLE_RETURN(std::string()); +} + +#endif // defined(BOOST_WINDOWS) + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/core.cpp b/src/boost/libs/log/src/core.cpp new file mode 100644 index 00000000..07f71996 --- /dev/null +++ b/src/boost/libs/log/src/core.cpp @@ -0,0 +1,766 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 core.cpp + * \author Andrey Semashev + * \date 19.04.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <new> +#include <vector> +#include <algorithm> +#include <boost/cstdint.hpp> +#include <boost/assert.hpp> +#include <boost/core/swap.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/smart_ptr/weak_ptr.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/range/iterator_range_core.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/random/taus88.hpp> +#include <boost/move/core.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/log/core/core.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/core/record_view.hpp> +#include <boost/log/sinks/sink.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/detail/singleton.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/tss.hpp> +#include <boost/thread/exceptions.hpp> +#include <boost/log/detail/locks.hpp> +#include <boost/log/detail/light_rw_mutex.hpp> +#include <boost/log/detail/thread_id.hpp> +#endif +#include "unique_ptr.hpp" +#include "default_sink.hpp" +#include "stateless_allocator.hpp" +#include "alignment_gap_between.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! Sequence shuffling algorithm. Very similar to std::random_shuffle, used for forward portability with compilers that removed it from the standard library (C++17). +template< typename Iterator, typename RandomNumberGenerator > +void random_shuffle(Iterator begin, Iterator end, RandomNumberGenerator& rng) +{ + Iterator it = begin; + ++it; + while (it != end) + { + Iterator where = begin + rng() % (it - begin + 1u); + if (where != it) + boost::swap(*where, *it); + ++it; + } +} + +} // namespace + +} // namespace aux + +//! Private record data information, with core-specific structures +struct record_view::private_data : + public public_data +{ + //! Underlying memory allocator + typedef boost::log::aux::stateless_allocator< char > stateless_allocator; + //! Sink pointer type + typedef weak_ptr< sinks::sink > sink_ptr; + //! Iterator range with pointers to the accepting sinks + typedef iterator_range< sink_ptr* > sink_list; + +private: + //! Number of sinks accepting the record + uint32_t m_accepting_sink_count; + //! Maximum number of sinks accepting the record + const uint32_t m_accepting_sink_capacity; + //! The flag indicates that the record has to be detached from the current thread + bool m_detach_from_thread_needed; + +private: + //! Initializing constructor + private_data(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity) BOOST_NOEXCEPT : + public_data(boost::move(values)), + m_accepting_sink_count(0), + m_accepting_sink_capacity(capacity), + m_detach_from_thread_needed(false) + { + } + +public: + //! Creates the object with the specified capacity + static private_data* create(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity) + { + private_data* p = reinterpret_cast< private_data* >(stateless_allocator().allocate + ( + sizeof(private_data) + + boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value + + capacity * sizeof(sink_ptr) + )); + new (p) private_data(boost::move(values), capacity); + return p; + } + + //! Destroys the object and frees the underlying storage + void destroy() BOOST_NOEXCEPT + { + sink_ptr* psink = begin(); + for (uint32_t i = 0u, n = m_accepting_sink_count; i < n; ++i) + { + psink[i].~sink_ptr(); + } + + const uint32_t capacity = m_accepting_sink_capacity; + this->~private_data(); + + stateless_allocator().deallocate + ( + reinterpret_cast< stateless_allocator::pointer >(this), + sizeof(private_data) + + boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value + + capacity * sizeof(sink_ptr) + ); + } + + //! Returns iterator range with the pointers to the accepting sinks + sink_list get_accepting_sinks() BOOST_NOEXCEPT + { + sink_ptr* p = begin(); + return sink_list(p, p + m_accepting_sink_count); + } + + //! Adds an accepting sink + void push_back_accepting_sink(shared_ptr< sinks::sink > const& sink) + { + BOOST_ASSERT(m_accepting_sink_count < m_accepting_sink_capacity); + sink_ptr* p = begin() + m_accepting_sink_count; + new (p) sink_ptr(sink); + ++m_accepting_sink_count; + m_detach_from_thread_needed |= sink->is_cross_thread(); + } + + //! Returns the number of accepting sinks + uint32_t accepting_sink_count() const BOOST_NOEXCEPT { return m_accepting_sink_count; } + + //! Returns the flag indicating whether it is needed to detach the record from the current thread + bool is_detach_from_thread_needed() const BOOST_NOEXCEPT { return m_detach_from_thread_needed; } + + BOOST_DELETED_FUNCTION(private_data(private_data const&)) + BOOST_DELETED_FUNCTION(private_data& operator= (private_data const&)) + +private: + //! Returns a pointer to the first accepting sink + sink_ptr* begin() BOOST_NOEXCEPT + { + return reinterpret_cast< sink_ptr* > + ( + reinterpret_cast< char* >(this) + + sizeof(private_data) + + boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value + ); + } +}; + +//! Destructor +BOOST_LOG_API void record_view::public_data::destroy(const public_data* p) BOOST_NOEXCEPT +{ + const_cast< private_data* >(static_cast< const private_data* >(p))->destroy(); +} + +//! The function ensures that the log record does not depend on any thread-specific data. +BOOST_LOG_API record_view record::lock() +{ + BOOST_ASSERT(m_impl != NULL); + + record_view::private_data* const impl = static_cast< record_view::private_data* >(m_impl); + if (impl->is_detach_from_thread_needed()) + { + attribute_value_set::const_iterator + it = impl->m_attribute_values.begin(), + end = impl->m_attribute_values.end(); + for (; it != end; ++it) + { + // Yep, a bit hackish. I'll need a better backdoor to do it gracefully. + const_cast< attribute_value_set::mapped_type& >(it->second).detach_from_thread(); + } + } + + // Move the implementation to the view + m_impl = NULL; + return record_view(impl); +} + +//! Logging system implementation +struct core::implementation : + public log::aux::lazy_singleton< + implementation, + core_ptr + > +{ +public: + //! Base type of singleton holder + typedef log::aux::lazy_singleton< + implementation, + core_ptr + > base_type; + +#if !defined(BOOST_LOG_NO_THREADS) + //! Read lock type + typedef log::aux::shared_lock_guard< log::aux::light_rw_mutex > scoped_read_lock; + //! Write lock type + typedef log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > scoped_write_lock; +#endif + + //! Sinks container type + typedef std::vector< shared_ptr< sinks::sink > > sink_list; + + //! Thread-specific data + struct thread_data + { + //! Thread-specific attribute set + attribute_set m_thread_attributes; + //! Random number generator for shuffling + random::taus88 m_rng; + + thread_data() : m_rng(get_random_seed()) + { + } + + private: + //! Creates a seed for RNG + static uint32_t get_random_seed() + { + uint32_t seed = static_cast< uint32_t >(posix_time::microsec_clock::universal_time().time_of_day().ticks()); +#if !defined(BOOST_LOG_NO_THREADS) + seed += static_cast< uint32_t >(log::aux::this_thread::get_id().native_id()); +#endif + return seed; + } + }; + +public: +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + log::aux::light_rw_mutex m_mutex; +#endif + + //! List of sinks involved into output + sink_list m_sinks; + //! Default sink + const shared_ptr< sinks::sink > m_default_sink; + + //! Global attribute set + attribute_set m_global_attributes; +#if !defined(BOOST_LOG_NO_THREADS) + //! Thread-specific data + thread_specific_ptr< thread_data > m_thread_data; + +#if defined(BOOST_LOG_USE_COMPILER_TLS) + //! Cached pointer to the thread-specific data + static BOOST_LOG_TLS thread_data* m_thread_data_cache; +#endif + +#else + //! Thread-specific data + log::aux::unique_ptr< thread_data > m_thread_data; +#endif + + //! The global state of logging + volatile bool m_enabled; + //! Global filter + filter m_filter; + + //! Exception handler + exception_handler_type m_exception_handler; + +public: + //! Constructor + implementation() : + m_default_sink(boost::make_shared< sinks::aux::default_sink >()), + m_enabled(true) + { + } + + //! Opens a record + template< typename SourceAttributesT > + BOOST_FORCEINLINE record open_record(BOOST_FWD_REF(SourceAttributesT) source_attributes) + { + record_view::private_data* rec_impl = NULL; + bool invoke_exception_handler = true; + + // Try a quick win first + if (m_enabled) try + { + thread_data* tsd = get_thread_data(); + + // Lock the core to be safe against any attribute or sink set modifications + BOOST_LOG_EXPR_IF_MT(scoped_read_lock lock(m_mutex);) + + if (m_enabled) + { + // Compose a view of attribute values (unfrozen, yet) + attribute_value_set attr_values(boost::forward< SourceAttributesT >(source_attributes), tsd->m_thread_attributes, m_global_attributes); + if (m_filter(attr_values)) + { + // The global filter passed, trying the sinks + attribute_value_set* values = &attr_values; + + // apply_sink_filter will invoke the exception handler if it has to + invoke_exception_handler = false; + + if (!m_sinks.empty()) + { + uint32_t remaining_capacity = static_cast< uint32_t >(m_sinks.size()); + sink_list::iterator it = m_sinks.begin(), end = m_sinks.end(); + for (; it != end; ++it, --remaining_capacity) + { + apply_sink_filter(*it, rec_impl, values, remaining_capacity); + } + } + else + { + // Use the default sink + apply_sink_filter(m_default_sink, rec_impl, values, 1); + } + + invoke_exception_handler = true; + + if (rec_impl && rec_impl->accepting_sink_count() == 0) + { + // No sinks accepted the record + rec_impl->destroy(); + rec_impl = NULL; + goto done; + } + + // Some sinks have accepted the record + values->freeze(); + } + } + } +#if !defined(BOOST_LOG_NO_THREADS) + catch (thread_interrupted&) + { + if (rec_impl) + rec_impl->destroy(); + throw; + } +#endif // !defined(BOOST_LOG_NO_THREADS) + catch (...) + { + if (rec_impl) + { + rec_impl->destroy(); + rec_impl = NULL; + } + + if (invoke_exception_handler) + { + // Lock the core to be safe against any attribute or sink set modifications + BOOST_LOG_EXPR_IF_MT(scoped_read_lock lock(m_mutex);) + if (m_exception_handler.empty()) + throw; + + m_exception_handler(); + } + else + throw; + } + + done: + return record(rec_impl); + } + + //! The method returns the current thread-specific data + thread_data* get_thread_data() + { +#if defined(BOOST_LOG_USE_COMPILER_TLS) + thread_data* p = m_thread_data_cache; +#else + thread_data* p = m_thread_data.get(); +#endif + if (!p) + { + init_thread_data(); +#if defined(BOOST_LOG_USE_COMPILER_TLS) + p = m_thread_data_cache; +#else + p = m_thread_data.get(); +#endif + } + return p; + } + + //! The function initializes the logging system + static void init_instance() + { + base_type::get_instance().reset(new core()); + } + +private: + //! The method initializes thread-specific data + void init_thread_data() + { + BOOST_LOG_EXPR_IF_MT(scoped_write_lock lock(m_mutex);) + if (!m_thread_data.get()) + { + log::aux::unique_ptr< thread_data > p(new thread_data()); + m_thread_data.reset(p.get()); +#if defined(BOOST_LOG_USE_COMPILER_TLS) + m_thread_data_cache = p.release(); +#else + p.release(); +#endif + } + } + + //! Invokes sink-specific filter and adds the sink to the record if the filter passes the log record + void apply_sink_filter(shared_ptr< sinks::sink > const& sink, record_view::private_data*& rec_impl, attribute_value_set*& attr_values, uint32_t remaining_capacity) + { + try + { + if (sink->will_consume(*attr_values)) + { + // If at least one sink accepts the record, it's time to create it + record_view::private_data* impl = rec_impl; + if (!impl) + { + rec_impl = impl = record_view::private_data::create(boost::move(*attr_values), remaining_capacity); + attr_values = &impl->m_attribute_values; + } + + impl->push_back_accepting_sink(sink); + } + } +#if !defined(BOOST_LOG_NO_THREADS) + catch (thread_interrupted&) + { + throw; + } +#endif // !defined(BOOST_LOG_NO_THREADS) + catch (...) + { + if (m_exception_handler.empty()) + throw; + m_exception_handler(); + } + } +}; + +#if defined(BOOST_LOG_USE_COMPILER_TLS) +//! Cached pointer to the thread-specific data +BOOST_LOG_TLS core::implementation::thread_data* core::implementation::m_thread_data_cache = NULL; +#endif // defined(BOOST_LOG_USE_COMPILER_TLS) + +//! Logging system constructor +core::core() : + m_impl(new implementation()) +{ +} + +//! Logging system destructor +core::~core() +{ + delete m_impl; + m_impl = NULL; +} + +//! The method returns a pointer to the logging system instance +BOOST_LOG_API core_ptr core::get() +{ + return implementation::get(); +} + +//! The method enables or disables logging and returns the previous state of logging flag +BOOST_LOG_API bool core::set_logging_enabled(bool enabled) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + const bool old_value = m_impl->m_enabled; + m_impl->m_enabled = enabled; + return old_value; +} + +//! The method allows to detect if logging is enabled +BOOST_LOG_API bool core::get_logging_enabled() const +{ + // Should have a read barrier here, but for performance reasons it is omitted. + // The function should be used as a quick check and doesn't need to be reliable. + return m_impl->m_enabled; +} + +//! The method adds a new sink +BOOST_LOG_API void core::add_sink(shared_ptr< sinks::sink > const& s) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + implementation::sink_list::iterator it = + std::find(m_impl->m_sinks.begin(), m_impl->m_sinks.end(), s); + if (it == m_impl->m_sinks.end()) + m_impl->m_sinks.push_back(s); +} + +//! The method removes the sink from the output +BOOST_LOG_API void core::remove_sink(shared_ptr< sinks::sink > const& s) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + implementation::sink_list::iterator it = + std::find(m_impl->m_sinks.begin(), m_impl->m_sinks.end(), s); + if (it != m_impl->m_sinks.end()) + m_impl->m_sinks.erase(it); +} + +//! The method removes all registered sinks from the output +BOOST_LOG_API void core::remove_all_sinks() +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + m_impl->m_sinks.clear(); +} + + +//! The method adds an attribute to the global attribute set +BOOST_LOG_API std::pair< attribute_set::iterator, bool > +core::add_global_attribute(attribute_name const& name, attribute const& attr) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + return m_impl->m_global_attributes.insert(name, attr); +} + +//! The method removes an attribute from the global attribute set +BOOST_LOG_API void core::remove_global_attribute(attribute_set::iterator it) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + m_impl->m_global_attributes.erase(it); +} + +//! The method returns the complete set of currently registered global attributes +BOOST_LOG_API attribute_set core::get_global_attributes() const +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);) + return m_impl->m_global_attributes; +} + +//! The method replaces the complete set of currently registered global attributes with the provided set +BOOST_LOG_API void core::set_global_attributes(attribute_set const& attrs) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + m_impl->m_global_attributes = attrs; +} + +//! The method adds an attribute to the thread-specific attribute set +BOOST_LOG_API std::pair< attribute_set::iterator, bool > +core::add_thread_attribute(attribute_name const& name, attribute const& attr) +{ + implementation::thread_data* p = m_impl->get_thread_data(); + return p->m_thread_attributes.insert(name, attr); +} + +//! The method removes an attribute from the thread-specific attribute set +BOOST_LOG_API void core::remove_thread_attribute(attribute_set::iterator it) +{ + implementation::thread_data* p = m_impl->get_thread_data(); + p->m_thread_attributes.erase(it); +} + +//! The method returns the complete set of currently registered thread-specific attributes +BOOST_LOG_API attribute_set core::get_thread_attributes() const +{ + implementation::thread_data* p = m_impl->get_thread_data(); + return p->m_thread_attributes; +} +//! The method replaces the complete set of currently registered thread-specific attributes with the provided set +BOOST_LOG_API void core::set_thread_attributes(attribute_set const& attrs) +{ + implementation::thread_data* p = m_impl->get_thread_data(); + p->m_thread_attributes = attrs; +} + +//! An internal method to set the global filter +BOOST_LOG_API void core::set_filter(filter const& filter) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + m_impl->m_filter = filter; +} + +//! The method removes the global logging filter +BOOST_LOG_API void core::reset_filter() +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + m_impl->m_filter.reset(); +} + +//! The method sets exception handler function +BOOST_LOG_API void core::set_exception_handler(exception_handler_type const& handler) +{ + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + m_impl->m_exception_handler = handler; +} + +//! The method performs flush on all registered sinks. +BOOST_LOG_API void core::flush() +{ + // Acquire exclusive lock to prevent any logging attempts while flushing + BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) + implementation::sink_list::iterator it = m_impl->m_sinks.begin(), end = m_impl->m_sinks.end(); + for (; it != end; ++it) + { + try + { + it->get()->flush(); + } +#if !defined(BOOST_LOG_NO_THREADS) + catch (thread_interrupted&) + { + throw; + } +#endif // !defined(BOOST_LOG_NO_THREADS) + catch (...) + { + if (m_impl->m_exception_handler.empty()) + throw; + m_impl->m_exception_handler(); + } + } +} + +//! The method attempts to open a new record to be written +BOOST_LOG_API record core::open_record(attribute_set const& source_attributes) +{ + return m_impl->open_record(source_attributes); +} + +//! The method attempts to open a new record to be written +BOOST_LOG_API record core::open_record(attribute_value_set const& source_attributes) +{ + return m_impl->open_record(source_attributes); +} + +//! The method attempts to open a new record to be written. +BOOST_LOG_API record core::open_record_move(attribute_value_set& source_attributes) +{ + return m_impl->open_record(boost::move(source_attributes)); +} + +//! The method pushes the record +BOOST_LOG_API void core::push_record_move(record& rec) +{ + try + { + record_view rec_view(rec.lock()); + record_view::private_data* data = static_cast< record_view::private_data* >(rec_view.m_impl.get()); + + typedef std::vector< shared_ptr< sinks::sink > > accepting_sinks_t; + accepting_sinks_t accepting_sinks(data->accepting_sink_count()); + shared_ptr< sinks::sink >* const begin = &*accepting_sinks.begin(); + shared_ptr< sinks::sink >* end = begin; + + // Lock sinks that are willing to consume the record + record_view::private_data::sink_list weak_sinks = data->get_accepting_sinks(); + record_view::private_data::sink_list::iterator + weak_it = weak_sinks.begin(), + weak_end = weak_sinks.end(); + for (; weak_it != weak_end; ++weak_it) + { + shared_ptr< sinks::sink >& last = *end; + weak_it->lock().swap(last); + if (last.get()) + ++end; + } + + bool shuffled = (end - begin) <= 1; + shared_ptr< sinks::sink >* it = begin; + while (true) try + { + // First try to distribute load between different sinks + bool all_locked = true; + while (it != end) + { + if (it->get()->try_consume(rec_view)) + { + --end; + end->swap(*it); + all_locked = false; + } + else + ++it; + } + + it = begin; + if (begin != end) + { + if (all_locked) + { + // If all sinks are busy then block on any + if (!shuffled) + { + implementation::thread_data* tsd = m_impl->get_thread_data(); + log::aux::random_shuffle(begin, end, tsd->m_rng); + shuffled = true; + } + + it->get()->consume(rec_view); + --end; + end->swap(*it); + } + } + else + break; + } +#if !defined(BOOST_LOG_NO_THREADS) + catch (thread_interrupted&) + { + throw; + } +#endif // !defined(BOOST_LOG_NO_THREADS) + catch (...) + { + // Lock the core to be safe against any attribute or sink set modifications + BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);) + if (m_impl->m_exception_handler.empty()) + throw; + + m_impl->m_exception_handler(); + + // Skip the sink that failed to consume the record + --end; + end->swap(*it); + } + } +#if !defined(BOOST_LOG_NO_THREADS) + catch (thread_interrupted&) + { + throw; + } +#endif // !defined(BOOST_LOG_NO_THREADS) + catch (...) + { + // Lock the core to be safe against any attribute or sink set modifications + BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);) + if (m_impl->m_exception_handler.empty()) + throw; + + m_impl->m_exception_handler(); + } +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/date_time_format_parser.cpp b/src/boost/libs/log/src/date_time_format_parser.cpp new file mode 100644 index 00000000..5b1381ff --- /dev/null +++ b/src/boost/libs/log/src/date_time_format_parser.cpp @@ -0,0 +1,423 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 date_time_format_parser.cpp + * \author Andrey Semashev + * \date 30.09.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstring> +#include <string> +#include <algorithm> +#include <boost/spirit/include/karma_uint.hpp> +#include <boost/spirit/include/karma_generate.hpp> +#include <boost/range/iterator_range_core.hpp> +#include <boost/log/detail/attachable_sstream_buf.hpp> +#include <boost/log/detail/date_time_format_parser.hpp> +#include <boost/log/detail/header.hpp> + +namespace karma = boost::spirit::karma; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +template< typename CharT > +struct string_constants; + +#ifdef BOOST_LOG_USE_CHAR + +template< > +struct string_constants< char > +{ + typedef char char_type; + + static const char_type* iso_date_format() { return "%Y%m%d"; } + static const char_type* extended_iso_date_format() { return "%Y-%m-%d"; } + + static const char_type* iso_time_format() { return "%H%M%S"; } + static const char_type* extended_iso_time_format() { return "%H:%M:%S"; } + static const char_type* default_time_format() { return "%H:%M:%S.%f"; } +}; + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +template< > +struct string_constants< wchar_t > +{ + typedef wchar_t char_type; + + static const char_type* iso_date_format() { return L"%Y%m%d"; } + static const char_type* extended_iso_date_format() { return L"%Y-%m-%d"; } + + static const char_type* iso_time_format() { return L"%H%M%S"; } + static const char_type* extended_iso_time_format() { return L"%H:%M:%S"; } + static const char_type* default_time_format() { return L"%H:%M:%S.%f"; } +}; + +#endif // BOOST_LOG_USE_WCHAR_T + +template< typename CallbackT > +struct common_flags +{ + typedef CallbackT callback_type; + typedef typename callback_type::char_type char_type; + typedef std::basic_string< char_type > string_type; + + const char_type* parse(const char_type* begin, const char_type* end, callback_type& callback) + { + switch (begin[1]) + { + case '%': + m_literal.push_back('%'); + break; + + default: + flush(callback); + callback.on_placeholder(iterator_range< const char_type* >(begin, begin + 2)); + break; + } + + return begin + 2; + } + + void add_literal(const char_type* begin, const char_type* end) + { + m_literal.append(begin, end); + } + + void flush(callback_type& callback) + { + if (!m_literal.empty()) + { + const char_type* p = m_literal.c_str(); + callback.on_literal(iterator_range< const char_type* >(p, p + m_literal.size())); + m_literal.clear(); + } + } + +private: + string_type m_literal; +}; + +template< typename BaseT > +struct date_flags : + public BaseT +{ + typedef typename BaseT::callback_type callback_type; + typedef typename BaseT::char_type char_type; + + const char_type* parse(const char_type* begin, const char_type* end, callback_type& callback) + { + typedef string_constants< char_type > constants; + + switch (begin[1]) + { + case 'Y': + { + this->flush(callback); + + std::size_t len = end - begin; + if (len >= 8 && std::memcmp(begin, constants::extended_iso_date_format(), 8 * sizeof(char_type)) == 0) + { + callback.on_extended_iso_date(); + return begin + 8; + } + else if (len >= 6 && std::memcmp(begin, constants::iso_date_format(), 6 * sizeof(char_type)) == 0) + { + callback.on_iso_date(); + return begin + 6; + } + else + { + callback.on_full_year(); + } + } + break; + + case 'y': + this->flush(callback); + callback.on_short_year(); + break; + + case 'm': + this->flush(callback); + callback.on_numeric_month(); + break; + + case 'B': + this->flush(callback); + callback.on_full_month(); + break; + + case 'b': + this->flush(callback); + callback.on_short_month(); + break; + + case 'd': + this->flush(callback); + callback.on_month_day(true); + break; + + case 'e': + this->flush(callback); + callback.on_month_day(false); + break; + + case 'w': + this->flush(callback); + callback.on_numeric_week_day(); + break; + + case 'A': + this->flush(callback); + callback.on_full_week_day(); + break; + + case 'a': + this->flush(callback); + callback.on_short_week_day(); + break; + + default: + return BaseT::parse(begin, end, callback); + } + + return begin + 2; + } +}; + +template< typename BaseT > +struct time_flags : + public BaseT +{ + typedef typename BaseT::callback_type callback_type; + typedef typename BaseT::char_type char_type; + + const char_type* parse(const char_type* begin, const char_type* end, callback_type& callback) + { + typedef string_constants< char_type > constants; + + switch (begin[1]) + { + case 'O': + case 'H': + { + this->flush(callback); + + std::size_t len = end - begin; + if (len >= 11 && std::memcmp(begin, constants::default_time_format(), 11 * sizeof(char_type)) == 0) + { + callback.on_default_time(); + return begin + 11; + } + else if (len >= 8 && std::memcmp(begin, constants::extended_iso_time_format(), 8 * sizeof(char_type)) == 0) + { + callback.on_extended_iso_time(); + return begin + 8; + } + else if (len >= 6 && std::memcmp(begin, constants::iso_time_format(), 6 * sizeof(char_type)) == 0) + { + callback.on_iso_time(); + return begin + 6; + } + else + { + callback.on_hours(true); + } + } + break; + + case 'T': + this->flush(callback); + callback.on_extended_iso_time(); + break; + + case 'k': + this->flush(callback); + callback.on_hours(false); + break; + + case 'I': + this->flush(callback); + callback.on_hours_12(true); + break; + + case 'l': + this->flush(callback); + callback.on_hours_12(false); + break; + + case 'M': + this->flush(callback); + callback.on_minutes(); + break; + + case 'S': + this->flush(callback); + callback.on_seconds(); + break; + + case 'f': + this->flush(callback); + callback.on_fractional_seconds(); + break; + + case 'P': + this->flush(callback); + callback.on_am_pm(false); + break; + + case 'p': + this->flush(callback); + callback.on_am_pm(true); + break; + + case 'Q': + this->flush(callback); + callback.on_extended_iso_time_zone(); + break; + + case 'q': + this->flush(callback); + callback.on_iso_time_zone(); + break; + + case '-': + this->flush(callback); + callback.on_duration_sign(false); + break; + + case '+': + this->flush(callback); + callback.on_duration_sign(true); + break; + + default: + return BaseT::parse(begin, end, callback); + } + + return begin + 2; + } +}; + +template< typename CharT, typename ParserT, typename CallbackT > +inline void parse_format(const CharT* begin, const CharT* end, ParserT& parser, CallbackT& callback) +{ + typedef CharT char_type; + + while (begin != end) + { + const char_type* p = std::find(begin, end, static_cast< char_type >('%')); + parser.add_literal(begin, p); + + if ((end - p) >= 2) + { + begin = parser.parse(p, end, callback); + } + else + { + if (p != end) + parser.add_literal(p, end); // a single '%' character at the end of the string + begin = end; + } + } + + parser.flush(callback); +} + +} // namespace + +//! Parses the date format string and invokes the callback object +template< typename CharT > +BOOST_LOG_API void parse_date_format(const CharT* begin, const CharT* end, date_format_parser_callback< CharT >& callback) +{ + typedef CharT char_type; + typedef date_format_parser_callback< char_type > callback_type; + date_flags< common_flags< callback_type > > parser; + parse_format(begin, end, parser, callback); +} + +//! Parses the time format string and invokes the callback object +template< typename CharT > +BOOST_LOG_API void parse_time_format(const CharT* begin, const CharT* end, time_format_parser_callback< CharT >& callback) +{ + typedef CharT char_type; + typedef time_format_parser_callback< char_type > callback_type; + time_flags< common_flags< callback_type > > parser; + parse_format(begin, end, parser, callback); +} + +//! Parses the date and time format string and invokes the callback object +template< typename CharT > +BOOST_LOG_API void parse_date_time_format(const CharT* begin, const CharT* end, date_time_format_parser_callback< CharT >& callback) +{ + typedef CharT char_type; + typedef date_time_format_parser_callback< char_type > callback_type; + date_flags< time_flags< common_flags< callback_type > > > parser; + parse_format(begin, end, parser, callback); +} + +template< typename CharT > +BOOST_LOG_API void put_integer(boost::log::aux::basic_ostringstreambuf< CharT >& strbuf, uint32_t value, unsigned int width, CharT fill_char) +{ + typedef CharT char_type; + char_type buf[std::numeric_limits< uint32_t >::digits10 + 2]; + char_type* p = buf; + + typedef karma::uint_generator< uint32_t, 10 > uint_gen; + karma::generate(p, uint_gen(), value); + const std::size_t len = p - buf; + if (len < width) + strbuf.append(width - len, fill_char); + strbuf.append(buf, len); +} + +#ifdef BOOST_LOG_USE_CHAR + +template BOOST_LOG_API +void parse_date_format(const char* begin, const char* end, date_format_parser_callback< char >& callback); +template BOOST_LOG_API +void parse_time_format(const char* begin, const char* end, time_format_parser_callback< char >& callback); +template BOOST_LOG_API +void parse_date_time_format(const char* begin, const char* end, date_time_format_parser_callback< char >& callback); +template BOOST_LOG_API +void put_integer(boost::log::aux::basic_ostringstreambuf< char >& strbuf, uint32_t value, unsigned int width, char fill_char); + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +template BOOST_LOG_API +void parse_date_format(const wchar_t* begin, const wchar_t* end, date_format_parser_callback< wchar_t >& callback); +template BOOST_LOG_API +void parse_time_format(const wchar_t* begin, const wchar_t* end, time_format_parser_callback< wchar_t >& callback); +template BOOST_LOG_API +void parse_date_time_format(const wchar_t* begin, const wchar_t* end, date_time_format_parser_callback< wchar_t >& callback); +template BOOST_LOG_API +void put_integer(boost::log::aux::basic_ostringstreambuf< wchar_t >& strbuf, uint32_t value, unsigned int width, wchar_t fill_char); + +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/default_attribute_names.cpp b/src/boost/libs/log/src/default_attribute_names.cpp new file mode 100644 index 00000000..81bce3b9 --- /dev/null +++ b/src/boost/libs/log/src/default_attribute_names.cpp @@ -0,0 +1,122 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_attribute_names.cpp + * \author Andrey Semashev + * \date 13.07.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/log/detail/default_attribute_names.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +namespace default_attribute_names { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + class names : + public lazy_singleton< names, shared_ptr< names > > + { + private: + typedef lazy_singleton< names, shared_ptr< names > > base_type; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) + friend class lazy_singleton< names, shared_ptr< names > >; +#else + friend class base_type; +#endif + + public: + const attribute_name severity; + const attribute_name channel; + const attribute_name message; + const attribute_name line_id; + const attribute_name timestamp; + const attribute_name process_id; + const attribute_name thread_id; + + private: + names() : + severity("Severity"), + channel("Channel"), + message("Message"), + line_id("LineID"), + timestamp("TimeStamp"), + process_id("ProcessID"), + thread_id("ThreadID") + { + } + + static void init_instance() + { + get_instance().reset(new names()); + } + + public: + static names& get() + { + return *base_type::get(); + } + }; + +} // namespace + +BOOST_LOG_API attribute_name severity() +{ + return names::get().severity; +} + +BOOST_LOG_API attribute_name channel() +{ + return names::get().channel; +} + +BOOST_LOG_API attribute_name message() +{ + return names::get().message; +} + +BOOST_LOG_API attribute_name line_id() +{ + return names::get().line_id; +} + +BOOST_LOG_API attribute_name timestamp() +{ + return names::get().timestamp; +} + +BOOST_LOG_API attribute_name process_id() +{ + return names::get().process_id; +} + +BOOST_LOG_API attribute_name thread_id() +{ + return names::get().thread_id; +} + +} // namespace default_attribute_names + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/default_sink.cpp b/src/boost/libs/log/src/default_sink.cpp new file mode 100644 index 00000000..92587546 --- /dev/null +++ b/src/boost/libs/log/src/default_sink.cpp @@ -0,0 +1,228 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_sink.cpp + * \author Andrey Semashev + * \date 19.04.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstdio> +#include <boost/optional/optional.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/locks.hpp> +#include <boost/log/detail/thread_id.hpp> +#endif +#include <boost/log/detail/default_attribute_names.hpp> +#include <boost/date_time/microsec_time_clock.hpp> +#include <boost/date_time/time_resolution_traits.hpp> +#include <boost/date_time/gregorian/gregorian_types.hpp> +#include "default_sink.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +#if !defined(BOOST_LOG_NO_THREADS) + +namespace aux { + +// Defined in thread_id.cpp +void format_thread_id(char* buf, std::size_t size, thread::id tid); + +} // namespace aux + +#endif // !defined(BOOST_LOG_NO_THREADS) + +namespace sinks { + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! A special time point type that contains decomposed date and time to avoid excessive calculations +struct decomposed_time_point +{ + struct date_type : + public gregorian::greg_year_month_day + { + date_type(year_type y, month_type m, day_type d) : + gregorian::greg_year_month_day(y, m, d) + { + } + }; + + struct time_duration_type : + public date_time::micro_res + { + typedef date_time::micro_res rep_type; + + hour_type hours; + min_type minutes; + sec_type seconds; + fractional_seconds_type useconds; + + time_duration_type(hour_type h, min_type m, sec_type s, fractional_seconds_type u) : + hours(h), + minutes(m), + seconds(s), + useconds(u) + { + } + }; + + date_type date; + time_duration_type time; + + decomposed_time_point(date_type const& d, time_duration_type const& t) : + date(d), + time(t) + { + } +}; + +inline const char* severity_level_to_string(boost::log::trivial::severity_level lvl) +{ + switch (lvl) + { + case boost::log::trivial::trace: + return "[trace] "; + case boost::log::trivial::debug: + return "[debug] "; + case boost::log::trivial::info: + return "[info] "; + case boost::log::trivial::warning: + return "[warning]"; + case boost::log::trivial::error: + return "[error] "; + case boost::log::trivial::fatal: + return "[fatal] "; + default: + return "[-] "; + } +} + +struct message_printer +{ + typedef void result_type; + + explicit message_printer(boost::log::trivial::severity_level lvl) : m_level(lvl) + { + } + +#ifdef BOOST_LOG_USE_CHAR + + result_type operator() (std::string const& msg) const + { +#if !defined(BOOST_LOG_NO_THREADS) + char thread_id_buf[64]; + boost::log::aux::format_thread_id(thread_id_buf, sizeof(thread_id_buf), boost::log::aux::this_thread::get_id()); +#endif + + const decomposed_time_point now = date_time::microsec_clock< decomposed_time_point >::local_time(); + + std::printf("[%04u-%02u-%02u %02u:%02u:%02u.%06u] " +#if !defined(BOOST_LOG_NO_THREADS) + "[%s] " +#endif + "%s %s\n", + static_cast< unsigned int >(now.date.year), + static_cast< unsigned int >(now.date.month), + static_cast< unsigned int >(now.date.day), + static_cast< unsigned int >(now.time.hours), + static_cast< unsigned int >(now.time.minutes), + static_cast< unsigned int >(now.time.seconds), + static_cast< unsigned int >(now.time.useconds), +#if !defined(BOOST_LOG_NO_THREADS) + thread_id_buf, +#endif + severity_level_to_string(m_level), + msg.c_str()); + } + +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T + + result_type operator() (std::wstring const& msg) const + { +#if !defined(BOOST_LOG_NO_THREADS) + char thread_id_buf[64]; + boost::log::aux::format_thread_id(thread_id_buf, sizeof(thread_id_buf), boost::log::aux::this_thread::get_id()); +#endif + + const decomposed_time_point now = date_time::microsec_clock< decomposed_time_point >::local_time(); + + std::printf("[%04u-%02u-%02u %02u:%02u:%02u.%06u] " +#if !defined(BOOST_LOG_NO_THREADS) + "[%s] " +#endif + "%s %ls\n", + static_cast< unsigned int >(now.date.year), + static_cast< unsigned int >(now.date.month), + static_cast< unsigned int >(now.date.day), + static_cast< unsigned int >(now.time.hours), + static_cast< unsigned int >(now.time.minutes), + static_cast< unsigned int >(now.time.seconds), + static_cast< unsigned int >(now.time.useconds), +#if !defined(BOOST_LOG_NO_THREADS) + thread_id_buf, +#endif + severity_level_to_string(m_level), + msg.c_str()); + } + +#endif + +private: + const boost::log::trivial::severity_level m_level; +}; + +} // namespace + +default_sink::default_sink() : + sink(false), + m_severity_name(boost::log::aux::default_attribute_names::severity()), + m_message_name(boost::log::aux::default_attribute_names::message()), + m_severity_extractor(boost::log::trivial::info) +{ +} + +default_sink::~default_sink() +{ +} + +bool default_sink::will_consume(attribute_value_set const&) +{ + return true; +} + +void default_sink::consume(record_view const& rec) +{ + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex_type > lock(m_mutex);) + m_message_visitor(m_message_name, rec.attribute_values(), message_printer(m_severity_extractor(m_severity_name, rec).get())); +} + +void default_sink::flush() +{ + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex_type > lock(m_mutex);) + std::fflush(stdout); +} + +} // namespace aux + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/default_sink.hpp b/src/boost/libs/log/src/default_sink.hpp new file mode 100644 index 00000000..bdcd3f46 --- /dev/null +++ b/src/boost/libs/log/src/default_sink.hpp @@ -0,0 +1,75 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_sink.hpp + * \author Andrey Semashev + * \date 08.01.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_DEFAULT_SINK_HPP_INCLUDED_ +#define BOOST_LOG_DEFAULT_SINK_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/log/sinks/sink.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/attributes/value_extraction.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/attributes/fallback_policy.hpp> +#include <boost/log/expressions/message.hpp> +#include <boost/log/trivial.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/mutex.hpp> +#endif +#include <boost/log/detail/header.hpp> + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +namespace aux { + +//! The default sink to be used when no sinks are registered in the logging core +class default_sink : + public sink +{ +private: +#if !defined(BOOST_LOG_NO_THREADS) + typedef mutex mutex_type; + mutex_type m_mutex; +#endif + attribute_name const m_severity_name, m_message_name; + value_extractor< boost::log::trivial::severity_level, fallback_to_default< boost::log::trivial::severity_level > > const m_severity_extractor; + value_visitor_invoker< expressions::tag::message::value_type > m_message_visitor; + +public: + default_sink(); + ~default_sink(); + bool will_consume(attribute_value_set const&); + void consume(record_view const& rec); + void flush(); +}; + +} // namespace aux + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_DEFAULT_SINK_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/dump.cpp b/src/boost/libs/log/src/dump.cpp new file mode 100644 index 00000000..e23cb2b4 --- /dev/null +++ b/src/boost/libs/log/src/dump.cpp @@ -0,0 +1,275 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <ostream> +#include <boost/cstdint.hpp> +#include <boost/log/utility/manipulators/dump.hpp> +#if defined(_MSC_VER) && (defined(BOOST_LOG_USE_SSSE3) || defined(BOOST_LOG_USE_AVX2)) +#include <boost/winapi/dll.hpp> +#include <intrin.h> // __cpuid +#endif +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#if defined(BOOST_LOG_USE_SSSE3) +extern dump_data_char_t dump_data_char_ssse3; +extern dump_data_wchar_t dump_data_wchar_ssse3; +#if !defined(BOOST_NO_CXX11_CHAR16_T) +extern dump_data_char16_t dump_data_char16_ssse3; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) +extern dump_data_char32_t dump_data_char32_ssse3; +#endif +#endif +#if defined(BOOST_LOG_USE_AVX2) +extern dump_data_char_t dump_data_char_avx2; +extern dump_data_wchar_t dump_data_wchar_avx2; +#if !defined(BOOST_NO_CXX11_CHAR16_T) +extern dump_data_char16_t dump_data_char16_avx2; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) +extern dump_data_char32_t dump_data_char32_avx2; +#endif +#endif + +enum { stride = 256 }; + +extern const char g_hex_char_table[2][16] = +{ + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' } +}; + +template< typename CharT > +void dump_data_generic(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 = g_hex_char_table[(strm.flags() & std::ios_base::uppercase) != 0]; + 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); + } +} + +BOOST_LOG_API dump_data_char_t* dump_data_char = &dump_data_generic< char >; +BOOST_LOG_API dump_data_wchar_t* dump_data_wchar = &dump_data_generic< wchar_t >; +#if !defined(BOOST_NO_CXX11_CHAR16_T) +BOOST_LOG_API dump_data_char16_t* dump_data_char16 = &dump_data_generic< char16_t >; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) +BOOST_LOG_API dump_data_char32_t* dump_data_char32 = &dump_data_generic< char32_t >; +#endif + +#if defined(BOOST_LOG_USE_SSSE3) || defined(BOOST_LOG_USE_AVX2) + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +struct function_pointer_initializer +{ + function_pointer_initializer() + { + // First, let's check for the max supported cpuid function + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + cpuid(eax, ebx, ecx, edx); + + const uint32_t max_cpuid_function = eax; + if (max_cpuid_function >= 1) + { + eax = 1; + ebx = ecx = edx = 0; + cpuid(eax, ebx, ecx, edx); + + // Check for SSSE3 support + if (ecx & (1u << 9)) + enable_ssse3(); + +#if defined(BOOST_LOG_USE_AVX2) + if (max_cpuid_function >= 7) + { + // To check for AVX2 availability we also need to verify that OS supports it + // Check that OSXSAVE is supported by CPU + if (ecx & (1u << 27)) + { + // Check that it is used by the OS + bool mmstate = false; +#if defined(__GNUC__) + // Get the XFEATURE_ENABLED_MASK register + __asm__ __volatile__ + ( + "xgetbv\n\t" + : "=a" (eax), "=d" (edx) + : "c" (0) + ); + mmstate = (eax & 6U) == 6U; +#elif defined(BOOST_WINDOWS) + // MSVC does not have an intrinsic for xgetbv, we have to query OS + boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); + if (hKernel32) + { + typedef uint64_t (BOOST_WINAPI_WINAPI_CC* get_enabled_extended_features_t)(uint64_t); + get_enabled_extended_features_t get_enabled_extended_features = (get_enabled_extended_features_t)boost::winapi::get_proc_address(hKernel32, "GetEnabledExtendedFeatures"); + if (get_enabled_extended_features) + { + // XSTATE_MASK_LEGACY_SSE | XSTATE_MASK_GSSE == 6 + mmstate = get_enabled_extended_features(6u) == 6u; + } + } +#endif + + if (mmstate) + { + // Finally, check for AVX2 support in CPU + eax = 7; + ebx = ecx = edx = 0; + cpuid(eax, ebx, ecx, edx); + + if (ebx & (1U << 5)) + enable_avx2(); + } + } + } +#endif // defined(BOOST_LOG_USE_AVX2) + } + } + +private: + static void cpuid(uint32_t& eax, uint32_t& ebx, uint32_t& ecx, uint32_t& edx) + { +#if defined(__GNUC__) +#if (defined(__i386__) || defined(__VXWORKS__)) && (defined(__PIC__) || defined(__PIE__)) && !(defined(__clang__) || (defined(BOOST_GCC) && BOOST_GCC >= 50100)) + // Unless the compiler can do it automatically, we have to backup ebx in 32-bit PIC/PIE code because it is reserved by the ABI. + // For VxWorks ebx is reserved on 64-bit as well. +#if defined(__x86_64__) + uint64_t rbx = ebx; + __asm__ __volatile__ + ( + "xchgq %%rbx, %0\n\t" + "cpuid\n\t" + "xchgq %%rbx, %0\n\t" + : "+DS" (rbx), "+a" (eax), "+c" (ecx), "+d" (edx) + ); + ebx = static_cast< uint32_t >(rbx); +#else // defined(__x86_64__) + __asm__ __volatile__ + ( + "xchgl %%ebx, %0\n\t" + "cpuid\n\t" + "xchgl %%ebx, %0\n\t" + : "+DS" (ebx), "+a" (eax), "+c" (ecx), "+d" (edx) + ); +#endif // defined(__x86_64__) +#else + __asm__ __volatile__ + ( + "cpuid\n\t" + : "+a" (eax), "+b" (ebx), "+c" (ecx), "+d" (edx) + ); +#endif +#elif defined(_MSC_VER) + int regs[4] = {}; + __cpuid(regs, eax); + eax = regs[0]; + ebx = regs[1]; + ecx = regs[2]; + edx = regs[3]; +#else +#error Boost.Log: Unexpected compiler +#endif + } + + static void enable_ssse3() + { + dump_data_char = &dump_data_char_ssse3; + dump_data_wchar = &dump_data_wchar_ssse3; +#if !defined(BOOST_NO_CXX11_CHAR16_T) + dump_data_char16 = &dump_data_char16_ssse3; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) + dump_data_char32 = &dump_data_char32_ssse3; +#endif + } + +#if defined(BOOST_LOG_USE_AVX2) + static void enable_avx2() + { + dump_data_char = &dump_data_char_avx2; + dump_data_wchar = &dump_data_wchar_avx2; +#if !defined(BOOST_NO_CXX11_CHAR16_T) + dump_data_char16 = &dump_data_char16_avx2; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) + dump_data_char32 = &dump_data_char32_avx2; +#endif + } +#endif // defined(BOOST_LOG_USE_AVX2) +}; + +static function_pointer_initializer g_function_pointer_initializer; + +} // namespace + +#endif // defined(BOOST_LOG_USE_SSSE3) || defined(BOOST_LOG_USE_AVX2) + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/dump_avx2.cpp b/src/boost/libs/log/src/dump_avx2.cpp new file mode 100644 index 00000000..4ab12500 --- /dev/null +++ b/src/boost/libs/log/src/dump_avx2.cpp @@ -0,0 +1,349 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_avx2.cpp + * \author Andrey Semashev + * \date 05.05.2013 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +// NOTE: You should generally avoid including headers as much as possible here, because this file +// is compiled with special compiler options, and any included header may result in generation of +// unintended code with these options and violation of ODR. +#include <boost/log/detail/config.hpp> +#include <ostream> +#include <immintrin.h> +#include <boost/cstdint.hpp> +#include <boost/log/detail/header.hpp> + +#if defined(__x86_64) || defined(__x86_64__) || \ + defined(__amd64__) || defined(__amd64) || \ + defined(_M_X64) +#define BOOST_LOG_AUX_X86_64 +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +extern const char g_hex_char_table[2][16]; + +template< typename CharT > +extern void dump_data_generic(const void* data, std::size_t size, std::basic_ostream< CharT >& strm); + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +enum +{ + packs_per_stride = 32, + stride = packs_per_stride * 32 +}; + +union ymm_constant +{ + uint8_t as_bytes[32]; + __m256i as_mm; + + BOOST_FORCEINLINE operator __m256i () const { return as_mm; } +}; + +static const ymm_constant mm_shuffle_pattern1 = {{ 0x80, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 0x80, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80 }}; +static const ymm_constant mm_shuffle_pattern2 = {{ 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10 }}; +static const ymm_constant mm_shuffle_pattern3 = {{ 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10, 11, 0x80, 12, 13, 0x80, 14, 15, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10, 11, 0x80, 12, 13, 0x80, 14, 15 }}; +static const ymm_constant mm_shuffle_pattern13 = {{ 0x80, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10, 11, 0x80, 12, 13, 0x80, 14, 15 }}; + +#if defined(BOOST_LOG_AUX_X86_64) + +// x86-64 architecture has more registers which we can utilize to pass constants +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL __m256i mm_15, __m256i mm_9, __m256i mm_char_0, __m256i mm_char_space, +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_15, mm_9, mm_char_0, mm_char_space, +#define BOOST_LOG_AUX_MM_CONSTANTS \ + const __m256i mm_15 = _mm256_set1_epi32(0x0F0F0F0F);\ + const __m256i mm_9 = _mm256_set1_epi32(0x09090909);\ + const __m256i mm_char_0 = _mm256_set1_epi32(0x30303030);\ + const __m256i mm_char_space = _mm256_set1_epi32(0x20202020); + +#else + +// MSVC in 32-bit mode is not able to pass all constants to dump_pack, and is also not able to align them on the stack, so we have to fetch them from global constants +static const ymm_constant mm_15 = {{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }}; +static const ymm_constant mm_9 = {{ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 }}; +static const ymm_constant mm_char_0 = {{ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }}; +static const ymm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }}; +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS +#define BOOST_LOG_AUX_MM_CONSTANTS + +#endif + +/*! + * \brief Dumps a pack of input data into a string of 8 bit ASCII characters. + * + * The composed string is placed as follows (in Intel notation): mm_output1[127:0], mm_output2[127:0], mm_output3[127:0], mm_output1[255:128], mm_output2[255:128], mm_output3[255:128]. + */ +static BOOST_FORCEINLINE void dump_pack +( + BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL + __m256i mm_char_10_to_a, __m256i mm_input, + __m256i& mm_output1, __m256i& mm_output2, __m256i& mm_output3 +) +{ + // Split half-bytes + __m256i mm_input_hi = _mm256_and_si256(_mm256_srli_epi16(mm_input, 4), mm_15); + __m256i mm_input_lo = _mm256_and_si256(mm_input, mm_15); + + // Stringize each of the halves + __m256i mm_addend_hi = _mm256_cmpgt_epi8(mm_input_hi, mm_9); + __m256i mm_addend_lo = _mm256_cmpgt_epi8(mm_input_lo, mm_9); + mm_addend_hi = _mm256_and_si256(mm_char_10_to_a, mm_addend_hi); + mm_addend_lo = _mm256_and_si256(mm_char_10_to_a, mm_addend_lo); + + mm_input_hi = _mm256_add_epi8(mm_input_hi, mm_char_0); + mm_input_lo = _mm256_add_epi8(mm_input_lo, mm_char_0); + + mm_input_hi = _mm256_add_epi8(mm_input_hi, mm_addend_hi); + mm_input_lo = _mm256_add_epi8(mm_input_lo, mm_addend_lo); + + // Join them back together + __m256i mm_1 = _mm256_unpacklo_epi8(mm_input_hi, mm_input_lo); + __m256i mm_2 = _mm256_unpackhi_epi8(mm_input_hi, mm_input_lo); + + // Insert spaces between stringized bytes: + // |0123456789abcdef|0123456789abcdef| + // | 01 23 45 67 89 |ab cd ef 01 23 4|5 67 89 ab cd ef| + __m256i mm_out1 = _mm256_shuffle_epi8(mm_1, mm_shuffle_pattern1.as_mm); + __m256i mm_out3 = _mm256_shuffle_epi8(mm_2, mm_shuffle_pattern3.as_mm); + __m256i mm_out2 = _mm256_shuffle_epi8(_mm256_alignr_epi8(mm_2, mm_1, 10), mm_shuffle_pattern2.as_mm); + + mm_output1 = _mm256_max_epu8(mm_out1, mm_char_space); + mm_output2 = _mm256_max_epu8(mm_out2, mm_char_space); + mm_output3 = _mm256_max_epu8(mm_out3, mm_char_space); +} + +//! Dumps a pack of input data into a string of 8 bit ASCII characters +static BOOST_FORCEINLINE void dump_pack +( + BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL + __m256i mm_char_10_to_a, __m128i mm_input, + __m128i& mm_output1, __m128i& mm_output2, __m128i& mm_output3 +) +{ + // Split half-bytes + __m128i mm_input_hi = _mm_srli_epi16(mm_input, 4); + __m256i mm = _mm256_inserti128_si256(_mm256_castsi128_si256(_mm_unpacklo_epi8(mm_input_hi, mm_input)), _mm_unpackhi_epi8(mm_input_hi, mm_input), 1); + mm = _mm256_and_si256(mm, mm_15); + + // Stringize the halves + __m256i mm_addend = _mm256_cmpgt_epi8(mm, mm_9); + mm_addend = _mm256_and_si256(mm_char_10_to_a, mm_addend); + + mm = _mm256_add_epi8(mm, mm_char_0); + mm = _mm256_add_epi8(mm, mm_addend); + + // Insert spaces between stringized bytes: + __m256i mm_out13 = _mm256_shuffle_epi8(mm, mm_shuffle_pattern13.as_mm); + __m128i mm_out2 = _mm_shuffle_epi8(_mm_alignr_epi8(_mm256_extracti128_si256(mm, 1), _mm256_castsi256_si128(mm), 10), _mm256_castsi256_si128(mm_shuffle_pattern2.as_mm)); + + mm_out13 = _mm256_max_epu8(mm_out13, mm_char_space); + mm_output2 = _mm_max_epu8(mm_out2, _mm256_castsi256_si128(mm_char_space)); + mm_output1 = _mm256_castsi256_si128(mm_out13); + mm_output3 = _mm256_extracti128_si256(mm_out13, 1); +} + +template< typename CharT > +BOOST_FORCEINLINE void store_characters(__m128i mm_chars, CharT* buf) +{ + switch (sizeof(CharT)) + { + case 1: + _mm_store_si128(reinterpret_cast< __m128i* >(buf), mm_chars); + break; + + case 2: + _mm256_store_si256(reinterpret_cast< __m256i* >(buf), _mm256_cvtepu8_epi16(mm_chars)); + break; + + case 4: + { + __m128i mm = _mm_unpackhi_epi64(mm_chars, mm_chars); + _mm256_store_si256(reinterpret_cast< __m256i* >(buf), _mm256_cvtepu8_epi32(mm_chars)); + _mm256_store_si256(reinterpret_cast< __m256i* >(buf) + 1, _mm256_cvtepu8_epi32(mm)); + } + break; + } +} + +template< typename CharT > +BOOST_FORCEINLINE void store_characters_x3(__m256i mm_chars1, __m256i mm_chars2, __m256i mm_chars3, CharT* buf) +{ + store_characters(_mm256_castsi256_si128(mm_chars1), buf); + store_characters(_mm256_castsi256_si128(mm_chars2), buf + 16); + store_characters(_mm256_castsi256_si128(mm_chars3), buf + 32); + store_characters(_mm256_extracti128_si256(mm_chars1, 1), buf + 48); + store_characters(_mm256_extracti128_si256(mm_chars2, 1), buf + 64); + store_characters(_mm256_extracti128_si256(mm_chars3, 1), buf + 80); +} + +template< typename CharT > +BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::basic_ostream< CharT >& strm) +{ + typedef CharT char_type; + + char_type buf_storage[stride * 3u + 32u]; + // Align the temporary buffer at 32 bytes + char_type* const buf = reinterpret_cast< char_type* >((uint8_t*)buf_storage + (32u - (((uintptr_t)(char_type*)buf_storage) & 31u))); + char_type* buf_begin = buf + 1u; // skip the first space of the first chunk + char_type* buf_end = buf + stride * 3u; + + __m256i mm_char_10_to_a; + if (strm.flags() & std::ios_base::uppercase) + mm_char_10_to_a = _mm256_set1_epi32(0x07070707); // '9' is 0x39 and 'A' is 0x41 in ASCII, so we have to add 0x07 to 0x3A to get uppercase letters + else + mm_char_10_to_a = _mm256_set1_epi32(0x27272727); // ...and 'a' is 0x61, which means we have to add 0x27 to 0x3A to get lowercase letters + + // First, check the input alignment. Also, if we can dump the whole data in one go, do it right away. It turns out to be faster than splitting + // the work between prealign and tail part. It is also a fairly common case since on most platforms memory is not aligned to 32 bytes (i.e. prealign is often needed). + const uint8_t* p = static_cast< const uint8_t* >(data); + const std::size_t prealign_size = size == 32u ? static_cast< std::size_t >(32u) : static_cast< std::size_t >((32u - ((uintptr_t)p & 31u)) & 31u); + if (prealign_size) + { + __m256i mm_input = _mm256_lddqu_si256(reinterpret_cast< const __m256i* >(p)); + BOOST_LOG_AUX_MM_CONSTANTS + + __m256i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters_x3(mm_output1, mm_output2, mm_output3, buf); + + _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compler generates around the function call + strm.write(buf_begin, prealign_size * 3u - 1u); + + buf_begin = buf; + size -= prealign_size; + p += prealign_size; + } + + const std::size_t stride_count = size / stride; + std::size_t tail_size = size % stride; + for (std::size_t i = 0; i < stride_count; ++i) + { + char_type* b = buf; + BOOST_LOG_AUX_MM_CONSTANTS + + for (unsigned int j = 0; j < packs_per_stride; ++j, b += 3u * 32u, p += 32u) + { + __m256i mm_input = _mm256_load_si256(reinterpret_cast< const __m256i* >(p)); + __m256i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters_x3(mm_output1, mm_output2, mm_output3, b); + } + + _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compler generates around the function call + strm.write(buf_begin, buf_end - buf_begin); + buf_begin = buf; + } + + if (BOOST_UNLIKELY(tail_size > 0)) + { + char_type* b = buf; + while (tail_size >= 16u) + { + __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); + BOOST_LOG_AUX_MM_CONSTANTS + + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, b); + store_characters(mm_output2, b + 16u); + store_characters(mm_output3, b + 32u); + + b += 3u * 16u; + p += 16u; + tail_size -= 16u; + } + + _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compler generates around the function call + const char* const char_table = g_hex_char_table[(strm.flags() & std::ios_base::uppercase) != 0]; + for (unsigned int i = 0; i < tail_size; ++i, ++p, b += 3u) + { + 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, b - buf_begin); + } +} + +} // namespace + +void dump_data_char_avx2(const void* data, std::size_t size, std::basic_ostream< char >& strm) +{ + if (size >= 32) + { + dump_data_avx2(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} + +void dump_data_wchar_avx2(const void* data, std::size_t size, std::basic_ostream< wchar_t >& strm) +{ + if (size >= 32) + { + dump_data_avx2(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} + +#if !defined(BOOST_NO_CXX11_CHAR16_T) +void dump_data_char16_avx2(const void* data, std::size_t size, std::basic_ostream< char16_t >& strm) +{ + if (size >= 32) + { + dump_data_avx2(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} +#endif + +#if !defined(BOOST_NO_CXX11_CHAR32_T) +void dump_data_char32_avx2(const void* data, std::size_t size, std::basic_ostream< char32_t >& strm) +{ + if (size >= 32) + { + dump_data_avx2(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/dump_ssse3.cpp b/src/boost/libs/log/src/dump_ssse3.cpp new file mode 100644 index 00000000..1325b49c --- /dev/null +++ b/src/boost/libs/log/src/dump_ssse3.cpp @@ -0,0 +1,311 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_ssse3.cpp + * \author Andrey Semashev + * \date 05.05.2013 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +// NOTE: You should generally avoid including headers as much as possible here, because this file +// is compiled with special compiler options, and any included header may result in generation of +// unintended code with these options and violation of ODR. +#include <boost/log/detail/config.hpp> +#include <ostream> +#include <tmmintrin.h> +#include <boost/cstdint.hpp> +#include <boost/log/detail/header.hpp> + +#if defined(__x86_64) || defined(__x86_64__) || \ + defined(__amd64__) || defined(__amd64) || \ + defined(_M_X64) +#define BOOST_LOG_AUX_X86_64 +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +extern const char g_hex_char_table[2][16]; + +template< typename CharT > +extern void dump_data_generic(const void* data, std::size_t size, std::basic_ostream< CharT >& strm); + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +enum +{ + packs_per_stride = 32, + stride = packs_per_stride * 16 +}; + +union xmm_constant +{ + uint8_t as_bytes[16]; + __m128i as_mm; + + BOOST_FORCEINLINE operator __m128i () const { return as_mm; } +}; + +static const xmm_constant mm_shuffle_pattern1 = {{ 0x80, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80 }}; +static const xmm_constant mm_shuffle_pattern2 = {{ 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10 }}; +static const xmm_constant mm_shuffle_pattern3 = {{ 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10, 11, 0x80, 12, 13, 0x80, 14, 15 }}; + +#if defined(BOOST_LOG_AUX_X86_64) + +// x86-64 architecture has more registers which we can utilize to pass constants +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL __m128i mm_15, __m128i mm_9, __m128i mm_char_0, __m128i mm_char_space, +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_15, mm_9, mm_char_0, mm_char_space, +#define BOOST_LOG_AUX_MM_CONSTANTS \ + const __m128i mm_15 = _mm_set1_epi32(0x0F0F0F0F);\ + const __m128i mm_9 = _mm_set1_epi32(0x09090909);\ + const __m128i mm_char_0 = _mm_set1_epi32(0x30303030);\ + const __m128i mm_char_space = _mm_set1_epi32(0x20202020); + +#else + +// MSVC in 32-bit mode is not able to pass all constants to dump_pack, and is also not able to align them on the stack, so we have to fetch them from global constants +static const xmm_constant mm_15 = {{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }}; +static const xmm_constant mm_9 = {{ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 }}; +static const xmm_constant mm_char_0 = {{ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }}; +static const xmm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }}; +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS +#define BOOST_LOG_AUX_MM_CONSTANTS + +#endif + +//! Dumps a pack of input data into a string of 8 bit ASCII characters +static BOOST_FORCEINLINE void dump_pack +( + BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL + __m128i mm_char_10_to_a, __m128i mm_input, + __m128i& mm_output1, __m128i& mm_output2, __m128i& mm_output3 +) +{ + // Split half-bytes + __m128i mm_input_hi = _mm_and_si128(_mm_srli_epi16(mm_input, 4), mm_15); + __m128i mm_input_lo = _mm_and_si128(mm_input, mm_15); + + // Stringize each of the halves + __m128i mm_addend_hi = _mm_cmpgt_epi8(mm_input_hi, mm_9); + __m128i mm_addend_lo = _mm_cmpgt_epi8(mm_input_lo, mm_9); + mm_addend_hi = _mm_and_si128(mm_char_10_to_a, mm_addend_hi); + mm_addend_lo = _mm_and_si128(mm_char_10_to_a, mm_addend_lo); + + mm_input_hi = _mm_add_epi8(mm_input_hi, mm_char_0); + mm_input_lo = _mm_add_epi8(mm_input_lo, mm_char_0); + + mm_input_hi = _mm_add_epi8(mm_input_hi, mm_addend_hi); + mm_input_lo = _mm_add_epi8(mm_input_lo, mm_addend_lo); + + // Join them back together + __m128i mm_1 = _mm_unpacklo_epi8(mm_input_hi, mm_input_lo); + __m128i mm_2 = _mm_unpackhi_epi8(mm_input_hi, mm_input_lo); + + // Insert spaces between stringized bytes: + // |0123456789abcdef|0123456789abcdef| + // | 01 23 45 67 89 |ab cd ef 01 23 4|5 67 89 ab cd ef| + mm_output1 = _mm_shuffle_epi8(mm_1, mm_shuffle_pattern1.as_mm); + mm_output2 = _mm_shuffle_epi8(_mm_alignr_epi8(mm_2, mm_1, 10), mm_shuffle_pattern2.as_mm); + mm_output3 = _mm_shuffle_epi8(mm_2, mm_shuffle_pattern3.as_mm); + + mm_output1 = _mm_max_epu8(mm_output1, mm_char_space); + mm_output2 = _mm_max_epu8(mm_output2, mm_char_space); + mm_output3 = _mm_max_epu8(mm_output3, mm_char_space); +} + +template< typename CharT > +BOOST_FORCEINLINE void store_characters(__m128i mm_chars, CharT* buf) +{ + switch (sizeof(CharT)) + { + case 1: + _mm_store_si128(reinterpret_cast< __m128i* >(buf), mm_chars); + break; + + case 2: + { + __m128i mm_0 = _mm_setzero_si128(); + _mm_store_si128(reinterpret_cast< __m128i* >(buf), _mm_unpacklo_epi8(mm_chars, mm_0)); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 1, _mm_unpackhi_epi8(mm_chars, mm_0)); + } + break; + + case 4: + { + __m128i mm_0 = _mm_setzero_si128(); + __m128i mm = _mm_unpacklo_epi8(mm_chars, mm_0); + _mm_store_si128(reinterpret_cast< __m128i* >(buf), _mm_unpacklo_epi16(mm, mm_0)); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 1, _mm_unpackhi_epi16(mm, mm_0)); + mm = _mm_unpackhi_epi8(mm_chars, mm_0); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 2, _mm_unpacklo_epi16(mm, mm_0)); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 3, _mm_unpackhi_epi16(mm, mm_0)); + } + break; + } +} + +template< typename CharT > +BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std::basic_ostream< CharT >& strm) +{ + typedef CharT char_type; + + char_type buf_storage[stride * 3u + 16u]; + // Align the temporary buffer at 16 bytes + char_type* const buf = reinterpret_cast< char_type* >((uint8_t*)buf_storage + (16u - (((uintptr_t)(char_type*)buf_storage) & 15u))); + char_type* buf_begin = buf + 1u; // skip the first space of the first chunk + char_type* buf_end = buf + stride * 3u; + + __m128i mm_char_10_to_a; + if (strm.flags() & std::ios_base::uppercase) + mm_char_10_to_a = _mm_set1_epi32(0x07070707); // '9' is 0x39 and 'A' is 0x41 in ASCII, so we have to add 0x07 to 0x3A to get uppercase letters + else + mm_char_10_to_a = _mm_set1_epi32(0x27272727); // ...and 'a' is 0x61, which means we have to add 0x27 to 0x3A to get lowercase letters + + // First, check the input alignment + const uint8_t* p = static_cast< const uint8_t* >(data); + const std::size_t prealign_size = ((16u - ((uintptr_t)p & 15u)) & 15u); + if (BOOST_UNLIKELY(prealign_size > 0)) + { + __m128i mm_input = _mm_lddqu_si128(reinterpret_cast< const __m128i* >(p)); + BOOST_LOG_AUX_MM_CONSTANTS + + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, buf); + store_characters(mm_output2, buf + 16u); + store_characters(mm_output3, buf + 32u); + + strm.write(buf_begin, prealign_size * 3u - 1u); + + buf_begin = buf; + size -= prealign_size; + p += prealign_size; + } + + const std::size_t stride_count = size / stride; + std::size_t tail_size = size % stride; + for (std::size_t i = 0; i < stride_count; ++i) + { + char_type* b = buf; + BOOST_LOG_AUX_MM_CONSTANTS + + for (unsigned int j = 0; j < packs_per_stride; ++j, b += 3u * 16u, p += 16u) + { + __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, b); + store_characters(mm_output2, b + 16u); + store_characters(mm_output3, b + 32u); + } + + strm.write(buf_begin, buf_end - buf_begin); + buf_begin = buf; + } + + if (BOOST_UNLIKELY(tail_size > 0)) + { + char_type* b = buf; + while (tail_size >= 16u) + { + __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); + BOOST_LOG_AUX_MM_CONSTANTS + + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, b); + store_characters(mm_output2, b + 16u); + store_characters(mm_output3, b + 32u); + + b += 3u * 16u; + p += 16u; + tail_size -= 16u; + } + + const char* const char_table = g_hex_char_table[(strm.flags() & std::ios_base::uppercase) != 0]; + for (unsigned int i = 0; i < tail_size; ++i, ++p, b += 3u) + { + 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, b - buf_begin); + } +} + +} // namespace + +void dump_data_char_ssse3(const void* data, std::size_t size, std::basic_ostream< char >& strm) +{ + if (size >= 16) + { + dump_data_ssse3(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} + +void dump_data_wchar_ssse3(const void* data, std::size_t size, std::basic_ostream< wchar_t >& strm) +{ + if (size >= 16) + { + dump_data_ssse3(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} + +#if !defined(BOOST_NO_CXX11_CHAR16_T) +void dump_data_char16_ssse3(const void* data, std::size_t size, std::basic_ostream< char16_t >& strm) +{ + if (size >= 16) + { + dump_data_ssse3(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} +#endif + +#if !defined(BOOST_NO_CXX11_CHAR32_T) +void dump_data_char32_ssse3(const void* data, std::size_t size, std::basic_ostream< char32_t >& strm) +{ + if (size >= 16) + { + dump_data_ssse3(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/event.cpp b/src/boost/libs/log/src/event.cpp new file mode 100644 index 00000000..ea9eee2f --- /dev/null +++ b/src/boost/libs/log/src/event.cpp @@ -0,0 +1,277 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 event.cpp + * \author Andrey Semashev + * \date 24.07.2011 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> + +#ifndef BOOST_LOG_NO_THREADS + +#include <boost/assert.hpp> +#include <boost/cstdint.hpp> +#include <boost/throw_exception.hpp> +#include <boost/log/detail/event.hpp> +#include <boost/log/exceptions.hpp> + +#if defined(BOOST_LOG_EVENT_USE_FUTEX) + +#include <stddef.h> +#include <errno.h> +#include <sys/syscall.h> +#include <linux/futex.h> +#include <boost/memory_order.hpp> + +// Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex +#if defined(SYS_futex) +#define BOOST_LOG_SYS_FUTEX SYS_futex +#else +#define BOOST_LOG_SYS_FUTEX __NR_futex +#endif + +#if defined(FUTEX_WAIT_PRIVATE) +#define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT_PRIVATE +#else +#define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT +#endif + +#if defined(FUTEX_WAKE_PRIVATE) +#define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE_PRIVATE +#else +#define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE +#endif + +#elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE) + +#include <errno.h> +#include <semaphore.h> +#include <boost/memory_order.hpp> +#include <boost/atomic/fences.hpp> + +#elif defined(BOOST_LOG_EVENT_USE_WINAPI) + +#include <windows.h> +#include <boost/detail/interlocked.hpp> + +#else + +#include <boost/thread/locks.hpp> + +#endif + +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#if defined(BOOST_LOG_EVENT_USE_FUTEX) + +//! Default constructor +BOOST_LOG_API futex_based_event::futex_based_event() : m_state(0) +{ +} + +//! Destructor +BOOST_LOG_API futex_based_event::~futex_based_event() +{ +} + +//! Waits for the object to become signalled +BOOST_LOG_API void futex_based_event::wait() +{ + if (m_state.exchange(0, boost::memory_order_acq_rel) == 0) + { + while (true) + { + if (::syscall(BOOST_LOG_SYS_FUTEX, &m_state.storage(), BOOST_LOG_FUTEX_WAIT, 0, NULL, NULL, 0) == 0) + { + // Another thread has set the event while sleeping + break; + } + + const int err = errno; + if (err == EWOULDBLOCK) + { + // Another thread has set the event before sleeping + break; + } + else if (BOOST_UNLIKELY(err != EINTR)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on the futex", (err)); + } + } + + m_state.store(0, boost::memory_order_relaxed); + } +} + +//! Sets the object to a signalled state +BOOST_LOG_API void futex_based_event::set_signalled() +{ + if (m_state.exchange(1, boost::memory_order_release) == 0) + { + if (BOOST_UNLIKELY(::syscall(BOOST_LOG_SYS_FUTEX, &m_state.storage(), BOOST_LOG_FUTEX_WAKE, 1, NULL, NULL, 0) < 0)) + { + const int err = errno; + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake threads blocked on the futex", (err)); + } + } +} + +#elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE) + +//! Default constructor +BOOST_LOG_API sem_based_event::sem_based_event() : m_state() +{ + if (BOOST_UNLIKELY(sem_init(&m_semaphore, 0, 0) != 0)) + { + const int err = errno; + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to initialize semaphore", (err)); + } +} + +//! Destructor +BOOST_LOG_API sem_based_event::~sem_based_event() +{ + BOOST_VERIFY(sem_destroy(&m_semaphore) == 0); +} + +//! Waits for the object to become signalled +BOOST_LOG_API void sem_based_event::wait() +{ + boost::atomic_thread_fence(boost::memory_order_acq_rel); + while (true) + { + if (sem_wait(&m_semaphore) != 0) + { + const int err = errno; + if (BOOST_UNLIKELY(err != EINTR)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on the semaphore", (err)); + } + } + else + break; + } + m_state.clear(boost::memory_order_relaxed); +} + +//! Sets the object to a signalled state +BOOST_LOG_API void sem_based_event::set_signalled() +{ + if (!m_state.test_and_set(boost::memory_order_release)) + { + if (BOOST_UNLIKELY(sem_post(&m_semaphore) != 0)) + { + const int err = errno; + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake the blocked thread", (err)); + } + } +} + +#elif defined(BOOST_LOG_EVENT_USE_WINAPI) + +//! Default constructor +BOOST_LOG_API winapi_based_event::winapi_based_event() : + m_state(0), + m_event(CreateEventA(NULL, false, false, NULL)) +{ + if (BOOST_UNLIKELY(!m_event)) + { + const DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to create Windows event", (err)); + } +} + +//! Destructor +BOOST_LOG_API winapi_based_event::~winapi_based_event() +{ + BOOST_VERIFY(CloseHandle(m_event) != 0); +} + +//! Waits for the object to become signalled +BOOST_LOG_API void winapi_based_event::wait() +{ + // On Windows we assume that memory view is always actual (Intel x86 and x86_64 arch) + if (const_cast< volatile boost::uint32_t& >(m_state) == 0) + { + if (BOOST_UNLIKELY(WaitForSingleObject(m_event, INFINITE) != 0)) + { + const DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on Windows event", (err)); + } + } + const_cast< volatile boost::uint32_t& >(m_state) = 0; +} + +//! Sets the object to a signalled state +BOOST_LOG_API void winapi_based_event::set_signalled() +{ + if (BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast< long* >(&m_state), 1, 0) == 0) + { + if (BOOST_UNLIKELY(SetEvent(m_event) == 0)) + { + const DWORD err = GetLastError(); + const_cast< volatile boost::uint32_t& >(m_state) = 0; + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake the blocked thread", (err)); + } + } +} + +#else + +//! Default constructor +BOOST_LOG_API generic_event::generic_event() : m_state(false) +{ +} + +//! Destructor +BOOST_LOG_API generic_event::~generic_event() +{ +} + +//! Waits for the object to become signalled +BOOST_LOG_API void generic_event::wait() +{ + boost::unique_lock< boost::mutex > lock(m_mutex); + while (!m_state) + { + m_cond.wait(lock); + } + m_state = false; +} + +//! Sets the object to a signalled state +BOOST_LOG_API void generic_event::set_signalled() +{ + boost::lock_guard< boost::mutex > lock(m_mutex); + if (!m_state) + { + m_state = true; + m_cond.notify_one(); + } +} + +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_NO_THREADS diff --git a/src/boost/libs/log/src/exceptions.cpp b/src/boost/libs/log/src/exceptions.cpp new file mode 100644 index 00000000..779b4311 --- /dev/null +++ b/src/boost/libs/log/src/exceptions.cpp @@ -0,0 +1,646 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 exceptions.cpp + * \author Andrey Semashev + * \date 31.10.2009 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/throw_exception.hpp> +#include <boost/exception/exception.hpp> +#include <boost/exception/errinfo_at_line.hpp> +#include <boost/exception/info.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/support/exception.hpp> +#include <boost/log/detail/header.hpp> + +#ifdef _MSC_VER +#pragma warning(push) +// conversion from 'size_t' to 'boost::error_info<boost::throw_line_,int>::value_type', possible loss of data +// No idea why line number is stored as a signed integer in the error info... +#pragma warning(disable: 4267) +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Attaches attribute name exception information +BOOST_LOG_API void attach_attribute_name_info(exception& e, attribute_name const& name) +{ + e << attribute_name_info(name); +} + +} // namespace aux + +bad_alloc::bad_alloc(const char* descr) : + m_message(descr) +{ +} + +bad_alloc::bad_alloc(std::string const& descr) : + m_message(descr) +{ +} + +bad_alloc::~bad_alloc() throw() +{ +} + +const char* bad_alloc::what() const throw() +{ + return m_message.c_str(); +} + +void bad_alloc::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(bad_alloc(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void bad_alloc::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(bad_alloc(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +capacity_limit_reached::capacity_limit_reached(const char* descr) : + bad_alloc(descr) +{ +} + +capacity_limit_reached::capacity_limit_reached(std::string const& descr) : + bad_alloc(descr) +{ +} + +capacity_limit_reached::~capacity_limit_reached() throw() +{ +} + +void capacity_limit_reached::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(capacity_limit_reached(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void capacity_limit_reached::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(capacity_limit_reached(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +runtime_error::runtime_error(std::string const& descr) : + std::runtime_error(descr) +{ +} + +runtime_error::~runtime_error() throw() +{ +} + +missing_value::missing_value() : + runtime_error("Requested value not found") +{ +} + +missing_value::missing_value(std::string const& descr) : + runtime_error(descr) +{ +} + +missing_value::~missing_value() throw() +{ +} + +void missing_value::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(missing_value()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void missing_value::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(missing_value(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void missing_value::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(missing_value(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void missing_value::throw_(const char* file, std::size_t line, const char* descr, attribute_name const& name) +{ + boost::throw_exception(boost::enable_error_info(missing_value(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + ); +} + +void missing_value::throw_(const char* file, std::size_t line, std::string const& descr, attribute_name const& name) +{ + boost::throw_exception(boost::enable_error_info(missing_value(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + ); +} + +invalid_type::invalid_type() : + runtime_error("Requested value has invalid type") +{ +} + +invalid_type::invalid_type(std::string const& descr) : + runtime_error(descr) +{ +} + +invalid_type::~invalid_type() throw() +{ +} + +void invalid_type::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(invalid_type()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, const char* descr, attribute_name const& name) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, std::string const& descr, attribute_name const& name) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, const char* descr, typeindex::type_index const& type) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << type_info_info(type) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, std::string const& descr, typeindex::type_index const& type) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << type_info_info(type) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, const char* descr, attribute_name const& name, typeindex::type_index const& type) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + << type_info_info(type) + ); +} + +void invalid_type::throw_(const char* file, std::size_t line, std::string const& descr, attribute_name const& name, typeindex::type_index const& type) +{ + boost::throw_exception(boost::enable_error_info(invalid_type(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + << type_info_info(type) + ); +} + +invalid_value::invalid_value() : + runtime_error("The value is invalid") +{ +} + +invalid_value::invalid_value(std::string const& descr) : + runtime_error(descr) +{ +} + +invalid_value::~invalid_value() throw() +{ +} + +void invalid_value::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(invalid_value()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void invalid_value::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(invalid_value(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void invalid_value::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(invalid_value(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +parse_error::parse_error() : + runtime_error("Failed to parse content") +{ +} + +parse_error::parse_error(std::string const& descr) : + runtime_error(descr) +{ +} + +parse_error::~parse_error() throw() +{ +} + +void parse_error::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(parse_error()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void parse_error::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(parse_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void parse_error::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(parse_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void parse_error::throw_(const char* file, std::size_t line, const char* descr, std::size_t content_line) +{ + boost::throw_exception(boost::enable_error_info(parse_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << boost::errinfo_at_line(content_line) + ); +} + +void parse_error::throw_(const char* file, std::size_t line, std::string const& descr, std::size_t content_line) +{ + boost::throw_exception(boost::enable_error_info(parse_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << boost::errinfo_at_line(content_line) + ); +} + +void parse_error::throw_(const char* file, std::size_t line, const char* descr, attribute_name const& name) +{ + boost::throw_exception(boost::enable_error_info(parse_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + ); +} + +void parse_error::throw_(const char* file, std::size_t line, std::string const& descr, attribute_name const& name) +{ + boost::throw_exception(boost::enable_error_info(parse_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + << attribute_name_info(name) + ); +} + +conversion_error::conversion_error() : + runtime_error("Failed to perform conversion") +{ +} + +conversion_error::conversion_error(std::string const& descr) : + runtime_error(descr) +{ +} + +conversion_error::~conversion_error() throw() +{ +} + +void conversion_error::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(conversion_error()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void conversion_error::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(conversion_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void conversion_error::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(conversion_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +system_error::system_error(boost::system::error_code code, std::string const& descr) : + boost::system::system_error(code, descr) +{ +} + +system_error::~system_error() throw() +{ +} + +void system_error::throw_(const char* file, std::size_t line, const char* descr, int system_error_code) +{ + boost::throw_exception(boost::enable_error_info(system_error(boost::system::error_code(system_error_code, boost::system::system_category()), descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void system_error::throw_(const char* file, std::size_t line, std::string const& descr, int system_error_code) +{ + boost::throw_exception(boost::enable_error_info(system_error(boost::system::error_code(system_error_code, boost::system::system_category()), descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void system_error::throw_(const char* file, std::size_t line, const char* descr, boost::system::error_code code) +{ + boost::throw_exception(boost::enable_error_info(system_error(code, descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void system_error::throw_(const char* file, std::size_t line, std::string const& descr, boost::system::error_code code) +{ + boost::throw_exception(boost::enable_error_info(system_error(code, descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +logic_error::logic_error(std::string const& descr) : + std::logic_error(descr) +{ +} + +logic_error::~logic_error() throw() +{ +} + +void logic_error::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(logic_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void logic_error::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(logic_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +odr_violation::odr_violation() : + logic_error("ODR violation detected") +{ +} + +odr_violation::odr_violation(std::string const& descr) : + logic_error(descr) +{ +} + +odr_violation::~odr_violation() throw() +{ +} + +void odr_violation::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(odr_violation()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void odr_violation::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(odr_violation(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void odr_violation::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(odr_violation(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +unexpected_call::unexpected_call() : + logic_error("Invalid call sequence") +{ +} + +unexpected_call::unexpected_call(std::string const& descr) : + logic_error(descr) +{ +} + +unexpected_call::~unexpected_call() throw() +{ +} + +void unexpected_call::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(unexpected_call()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void unexpected_call::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(unexpected_call(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void unexpected_call::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(unexpected_call(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +setup_error::setup_error() : + logic_error("The library is not initialized properly") +{ +} + +setup_error::setup_error(std::string const& descr) : + logic_error(descr) +{ +} + +setup_error::~setup_error() throw() +{ +} + +void setup_error::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(setup_error()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void setup_error::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(setup_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void setup_error::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(setup_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +limitation_error::limitation_error() : + logic_error("Boost.Log library limit reached") +{ +} + +limitation_error::limitation_error(std::string const& descr) : + logic_error(descr) +{ +} + +limitation_error::~limitation_error() throw() +{ +} + +void limitation_error::throw_(const char* file, std::size_t line) +{ + boost::throw_exception(boost::enable_error_info(limitation_error()) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void limitation_error::throw_(const char* file, std::size_t line, const char* descr) +{ + boost::throw_exception(boost::enable_error_info(limitation_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +void limitation_error::throw_(const char* file, std::size_t line, std::string const& descr) +{ + boost::throw_exception(boost::enable_error_info(limitation_error(descr)) + << boost::throw_file(file) + << boost::throw_line(line) + ); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/format_parser.cpp b/src/boost/libs/log/src/format_parser.cpp new file mode 100644 index 00000000..1215d6b2 --- /dev/null +++ b/src/boost/libs/log/src/format_parser.cpp @@ -0,0 +1,154 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 format_parser.cpp + * \author Andrey Semashev + * \date 16.11.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <string> +#include <algorithm> +#include <boost/throw_exception.hpp> +#include <boost/exception/exception.hpp> +#include <boost/move/core.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/spirit/include/qi_uint.hpp> +#include <boost/spirit/include/qi_parse.hpp> +#include <boost/log/detail/format.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/support/exception.hpp> +#include "spirit_encoding.hpp" +#include <boost/log/detail/header.hpp> + +namespace qi = boost::spirit::qi; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +template< typename CharT > +BOOST_LOG_API format_description< CharT > parse_format(const CharT* begin, const CharT* end) +{ + typedef CharT char_type; + typedef format_description< char_type > description; + typedef typename encoding< char_type >::type traits; + + const char_type* original_begin = begin; + description descr; + unsigned int literal_start_pos = 0; + + while (begin != end) + { + const char_type* p = std::find(begin, end, static_cast< char_type >('%')); + descr.literal_chars.append(begin, p); + + if ((end - p) >= 2) + { + // Check for a percent placeholder + char_type c = p[1]; + if (c == static_cast< char_type >('%')) + { + descr.literal_chars.push_back(static_cast< char_type >('%')); + begin = p + 2; + continue; + } + + // From here on, no more literals are possible. Append the literal element. + { + const unsigned int literal_chars_size = static_cast< unsigned int >(descr.literal_chars.size()); + if (literal_start_pos < literal_chars_size) + { + descr.format_elements.push_back(format_element::literal(literal_start_pos, literal_chars_size - literal_start_pos)); + literal_start_pos = literal_chars_size; + } + } + + // Check if this is a positional argument + if (traits::isdigit(c)) + { + if (c != static_cast< char_type >('0')) + { + // Positional argument in the form "%N%" + unsigned int n = 0; + const char_type* pp = p + 1; + qi::parse(pp, end, qi::uint_, n); + if (n == 0 || pp == end || *pp != static_cast< char_type >('%')) + { + boost::throw_exception(boost::enable_error_info(parse_error("Invalid positional format placeholder")) << boost::throw_file(__FILE__) << boost::throw_line(__LINE__) + << boost::log::position_info(static_cast< unsigned int >(p - original_begin)) + ); + } + + // Safety check against ridiculously large argument numbers which would lead to excessive memory consumption. + // This could be useful if the format string is gathered from an external source (e.g. a config file). + if (n > 1000) + { + boost::throw_exception(boost::enable_error_info(limitation_error("Positional format placeholder too big")) << boost::throw_file(__FILE__) << boost::throw_line(__LINE__) + << boost::log::position_info(static_cast< unsigned int >(p - original_begin)) + ); + } + + // We count positional arguments from 0, not from 1 as in format strings + descr.format_elements.push_back(format_element::positional_argument(n - 1)); + begin = pp + 1; // skip the closing '%' + + continue; + } + else + { + // This must be the filler character, not supported yet + } + } + + // This must be something else, not supported yet + boost::throw_exception(boost::enable_error_info(parse_error("Unsupported format placeholder")) << boost::throw_file(__FILE__) << boost::throw_line(__LINE__) + << boost::log::position_info(static_cast< unsigned int >(p - original_begin)) + ); + } + else + { + if (p != end) + descr.literal_chars.push_back(static_cast< char_type >('%')); // a single '%' character at the end of the string + begin = end; + } + } + + const unsigned int literal_chars_size = static_cast< unsigned int >(descr.literal_chars.size()); + if (literal_start_pos < literal_chars_size) + descr.format_elements.push_back(format_element::literal(literal_start_pos, literal_chars_size - literal_start_pos)); + + return BOOST_LOG_NRVO_RESULT(descr); +} + + +#ifdef BOOST_LOG_USE_CHAR + +template BOOST_LOG_API +format_description< char > parse_format(const char* begin, const char* end); + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +template BOOST_LOG_API +format_description< wchar_t > parse_format(const wchar_t* begin, const wchar_t* end); + +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/global_logger_storage.cpp b/src/boost/libs/log/src/global_logger_storage.cpp new file mode 100644 index 00000000..6ddcc1b7 --- /dev/null +++ b/src/boost/libs/log/src/global_logger_storage.cpp @@ -0,0 +1,110 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 global_logger_storage.cpp + * \author Andrey Semashev + * \date 21.04.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <map> +#include <string> +#include <boost/limits.hpp> +#include <boost/type_index.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/detail/snprintf.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/sources/global_logger_storage.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/mutex.hpp> +#include <boost/log/detail/locks.hpp> +#endif +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sources { + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! The loggers repository singleton +struct loggers_repository : + public log::aux::lazy_singleton< loggers_repository > +{ + //! Repository map type + typedef std::map< typeindex::type_index, shared_ptr< logger_holder_base > > loggers_map_t; + +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization primitive + mutable mutex m_Mutex; +#endif + //! Map of logger holders + loggers_map_t m_Loggers; +}; + +} // namespace + +//! Finds or creates the logger and returns its holder +BOOST_LOG_API shared_ptr< logger_holder_base > global_storage::get_or_init(typeindex::type_index key, initializer_t initializer) +{ + typedef loggers_repository::loggers_map_t loggers_map_t; + loggers_repository& repo = loggers_repository::get(); + + BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< mutex > lock(repo.m_Mutex);) + loggers_map_t::iterator it = repo.m_Loggers.find(key); + if (it != repo.m_Loggers.end()) + { + // There is an instance + return it->second; + } + else + { + // We have to create a logger instance + shared_ptr< logger_holder_base > inst = initializer(); + repo.m_Loggers[key] = inst; + return inst; + } +} + +//! Throws the \c odr_violation exception +BOOST_LOG_API BOOST_LOG_NORETURN void throw_odr_violation( + typeindex::type_index tag_type, + typeindex::type_index logger_type, + logger_holder_base const& registered) +{ + char buf[std::numeric_limits< unsigned int >::digits10 + 3]; + if (log::aux::snprintf(buf, sizeof(buf), "%u", registered.m_RegistrationLine) < 0) + buf[0] = '\0'; + std::string str = + std::string("Could not initialize global logger with tag \"") + + tag_type.pretty_name() + + "\" and type \"" + + logger_type.pretty_name() + + "\". A logger of type \"" + + registered.m_LoggerType.pretty_name() + + "\" with the same tag has already been registered at " + + registered.m_RegistrationFile + ":" + buf + "."; + + BOOST_LOG_THROW_DESCR(odr_violation, str); +} + +} // namespace aux + +} // namespace sources + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/id_formatting.hpp b/src/boost/libs/log/src/id_formatting.hpp new file mode 100644 index 00000000..b42c5128 --- /dev/null +++ b/src/boost/libs/log/src/id_formatting.hpp @@ -0,0 +1,61 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 id_formatting.hpp + * \author Andrey Semashev + * \date 25.01.2015 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_ID_FORMATTING_HPP_INCLUDED_ +#define BOOST_LOG_ID_FORMATTING_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +// Defined in dump.cpp +extern const char g_hex_char_table[2][16]; + +template< std::size_t IdSize, typename CharT, typename IdT > +inline void format_id(CharT* buf, std::size_t size, IdT id, bool uppercase) BOOST_NOEXCEPT +{ + const char* const char_table = g_hex_char_table[uppercase]; + + // Input buffer is assumed to be always larger than 2 chars + *buf++ = static_cast< CharT >(char_table[0]); // '0' + *buf++ = static_cast< CharT >(char_table[10] + ('x' - 'a')); // 'x' + + size -= 3; // reserve space for the terminating 0 + std::size_t i = 0; + const std::size_t n = (size > (IdSize * 2u)) ? IdSize * 2u : size; + for (std::size_t shift = n * 4u - 4u; i < n; ++i, shift -= 4u) + { + buf[i] = static_cast< CharT >(char_table[(id >> shift) & 15u]); + } + + buf[i] = static_cast< CharT >('\0'); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_ID_FORMATTING_HPP_INCLUDED_ + diff --git a/src/boost/libs/log/src/murmur3.hpp b/src/boost/libs/log/src/murmur3.hpp new file mode 100644 index 00000000..2968c084 --- /dev/null +++ b/src/boost/libs/log/src/murmur3.hpp @@ -0,0 +1,84 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 murmur3.hpp + * \author Andrey Semashev + * \date 16.01.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * This file implements MurmurHash3 hash function implementation. See https://en.wikipedia.org/wiki/MurmurHash. + */ + +#ifndef BOOST_LOG_MURMUR3_HPP_INCLUDED_ +#define BOOST_LOG_MURMUR3_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! 32-bit MurmurHash3 algorithm implementation (https://en.wikipedia.org/wiki/MurmurHash) +class murmur3_32 +{ +private: + uint32_t m_state; + uint32_t m_len; + + static BOOST_CONSTEXPR_OR_CONST uint32_t c1 = 0xcc9e2d51; + static BOOST_CONSTEXPR_OR_CONST uint32_t c2 = 0x1b873593; + static BOOST_CONSTEXPR_OR_CONST uint32_t r1 = 15; + static BOOST_CONSTEXPR_OR_CONST uint32_t r2 = 13; + static BOOST_CONSTEXPR_OR_CONST uint32_t m = 5; + static BOOST_CONSTEXPR_OR_CONST uint32_t n = 0xe6546b64; + +public: + explicit BOOST_CONSTEXPR murmur3_32(uint32_t seed) BOOST_NOEXCEPT : m_state(seed), m_len(0u) + { + } + + //! Mixing stage of the 32-bit MurmurHash3 algorithm + void mix(uint32_t value) BOOST_NOEXCEPT + { + value *= c1; + value = (value << r1) | (value >> (32u - r1)); + value *= c2; + + uint32_t h = m_state ^ value; + m_state = ((h << r2) | (h >> (32u - r2))) * m + n; + m_len += 4u; + } + + //! Finalization stage of the 32-bit MurmurHash3 algorithm + uint32_t finalize() BOOST_NOEXCEPT + { + uint32_t h = m_state ^ m_len; + h ^= h >> 16u; + h *= 0x85ebca6bu; + h ^= h >> 13u; + h *= 0xc2b2ae35u; + h ^= h >> 16u; + m_state = h; + return h; + } +}; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_MURMUR3_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/named_scope.cpp b/src/boost/libs/log/src/named_scope.cpp new file mode 100644 index 00000000..5fa634a3 --- /dev/null +++ b/src/boost/libs/log/src/named_scope.cpp @@ -0,0 +1,313 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 named_scope.cpp + * \author Andrey Semashev + * \date 24.06.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <utility> +#include <algorithm> +#include <boost/type_index.hpp> +#include <boost/optional/optional.hpp> +#include <boost/log/attributes/attribute.hpp> +#include <boost/log/attributes/attribute_value.hpp> +#include <boost/log/attributes/named_scope.hpp> +#include <boost/log/utility/type_dispatch/type_dispatcher.hpp> +#include <boost/log/detail/singleton.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/tss.hpp> +#endif +#include "unique_ptr.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace attributes { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + //! Actual implementation of the named scope list + class writeable_named_scope_list : + public named_scope_list + { + //! Base type + typedef named_scope_list base_type; + + public: + //! Const reference type + typedef base_type::const_reference const_reference; + + public: + //! The method pushes the scope to the back of the list + BOOST_FORCEINLINE void push_back(const_reference entry) BOOST_NOEXCEPT + { + aux::named_scope_list_node* top = this->m_RootNode._m_pPrev; + entry._m_pPrev = top; + entry._m_pNext = &this->m_RootNode; + + BOOST_LOG_ASSUME(&entry != 0); + this->m_RootNode._m_pPrev = top->_m_pNext = + const_cast< aux::named_scope_list_node* >( + static_cast< const aux::named_scope_list_node* >(&entry)); + + ++this->m_Size; + } + //! The method removes the top scope entry from the list + BOOST_FORCEINLINE void pop_back() BOOST_NOEXCEPT + { + aux::named_scope_list_node* top = this->m_RootNode._m_pPrev; + top->_m_pPrev->_m_pNext = top->_m_pNext; + top->_m_pNext->_m_pPrev = top->_m_pPrev; + --this->m_Size; + } + }; + + //! Named scope attribute value + class named_scope_value : + public attribute_value::impl + { + //! Scope names stack + typedef named_scope_list scope_stack; + + //! Pointer to the actual scope value + scope_stack* m_pValue; + //! A thread-independent value + optional< scope_stack > m_DetachedValue; + + public: + //! Constructor + explicit named_scope_value(scope_stack* p) : m_pValue(p) {} + + //! The method dispatches the value to the given object. It returns true if the + //! object was capable to consume the real attribute value type and false otherwise. + bool dispatch(type_dispatcher& dispatcher) + { + type_dispatcher::callback< scope_stack > callback = + dispatcher.get_callback< scope_stack >(); + if (callback) + { + callback(*m_pValue); + return true; + } + else + return false; + } + + /*! + * \return The attribute value type + */ + typeindex::type_index get_type() const { return typeindex::type_id< scope_stack >(); } + + //! The method is called when the attribute value is passed to another thread (e.g. + //! in case of asynchronous logging). The value should ensure it properly owns all thread-specific data. + intrusive_ptr< attribute_value::impl > detach_from_thread() + { + if (!m_DetachedValue) + { + m_DetachedValue = *m_pValue; + m_pValue = m_DetachedValue.get_ptr(); + } + + return this; + } + }; + +} // namespace + +//! Named scope attribute implementation +struct BOOST_SYMBOL_VISIBLE named_scope::impl : + public attribute::impl, + public log::aux::singleton< + impl, + intrusive_ptr< impl > + > +{ + //! Singleton base type + typedef log::aux::singleton< + impl, + intrusive_ptr< impl > + > singleton_base_type; + + //! Writable scope list type + typedef writeable_named_scope_list scope_list; + +#if !defined(BOOST_LOG_NO_THREADS) + //! Pointer to the thread-specific scope stack + thread_specific_ptr< scope_list > pScopes; + +#if defined(BOOST_LOG_USE_COMPILER_TLS) + //! Cached pointer to the thread-specific scope stack + static BOOST_LOG_TLS scope_list* pScopesCache; +#endif + +#else + //! Pointer to the scope stack + log::aux::unique_ptr< scope_list > pScopes; +#endif + + //! The method returns current thread scope stack + scope_list& get_scope_list() + { +#if defined(BOOST_LOG_USE_COMPILER_TLS) + scope_list* p = pScopesCache; +#else + scope_list* p = pScopes.get(); +#endif + if (!p) + { + log::aux::unique_ptr< scope_list > pNew(new scope_list()); + pScopes.reset(pNew.get()); +#if defined(BOOST_LOG_USE_COMPILER_TLS) + pScopesCache = p = pNew.release(); +#else + p = pNew.release(); +#endif + } + + return *p; + } + + //! Instance initializer + static void init_instance() + { + singleton_base_type::get_instance().reset(new impl()); + } + + //! The method returns the actual attribute value. It must not return NULL. + attribute_value get_value() + { + return attribute_value(new named_scope_value(&get_scope_list())); + } + +private: + impl() {} +}; + +#if defined(BOOST_LOG_USE_COMPILER_TLS) +//! Cached pointer to the thread-specific scope stack +BOOST_LOG_TLS named_scope::impl::scope_list* +named_scope::impl::pScopesCache = NULL; +#endif // defined(BOOST_LOG_USE_COMPILER_TLS) + + +//! Copy constructor +BOOST_LOG_API named_scope_list::named_scope_list(named_scope_list const& that) : + allocator_type(static_cast< allocator_type const& >(that)), + m_Size(that.size()), + m_fNeedToDeallocate(!that.empty()) +{ + if (m_Size > 0) + { + // Copy the container contents + pointer p = allocator_type::allocate(that.size()); + aux::named_scope_list_node* prev = &m_RootNode; + for (const_iterator src = that.begin(), end = that.end(); src != end; ++src, ++p) + { + allocator_type::construct(p, *src); // won't throw + p->_m_pPrev = prev; + prev->_m_pNext = p; + prev = p; + } + m_RootNode._m_pPrev = prev; + prev->_m_pNext = &m_RootNode; + } +} + +//! Destructor +BOOST_LOG_API named_scope_list::~named_scope_list() +{ + if (m_fNeedToDeallocate) + { + iterator it(m_RootNode._m_pNext); + iterator end(&m_RootNode); + while (it != end) + allocator_type::destroy(&*(it++)); + allocator_type::deallocate(static_cast< pointer >(m_RootNode._m_pNext), m_Size); + } +} + +//! Swaps two instances of the container +BOOST_LOG_API void named_scope_list::swap(named_scope_list& that) +{ + if (!this->empty()) + { + if (!that.empty()) + { + // both containers are not empty + std::swap(m_RootNode._m_pNext->_m_pPrev, that.m_RootNode._m_pNext->_m_pPrev); + std::swap(m_RootNode._m_pPrev->_m_pNext, that.m_RootNode._m_pPrev->_m_pNext); + std::swap(m_RootNode, that.m_RootNode); + std::swap(m_Size, that.m_Size); + std::swap(m_fNeedToDeallocate, that.m_fNeedToDeallocate); + } + else + { + // this is not empty + m_RootNode._m_pNext->_m_pPrev = m_RootNode._m_pPrev->_m_pNext = &that.m_RootNode; + that.m_RootNode = m_RootNode; + m_RootNode._m_pNext = m_RootNode._m_pPrev = &m_RootNode; + std::swap(m_Size, that.m_Size); + std::swap(m_fNeedToDeallocate, that.m_fNeedToDeallocate); + } + } + else if (!that.empty()) + { + // that is not empty + that.m_RootNode._m_pNext->_m_pPrev = that.m_RootNode._m_pPrev->_m_pNext = &m_RootNode; + m_RootNode = that.m_RootNode; + that.m_RootNode._m_pNext = that.m_RootNode._m_pPrev = &that.m_RootNode; + std::swap(m_Size, that.m_Size); + std::swap(m_fNeedToDeallocate, that.m_fNeedToDeallocate); + } +} + +//! Constructor +named_scope::named_scope() : + attribute(impl::instance) +{ +} + +//! Constructor for casting support +named_scope::named_scope(cast_source const& source) : + attribute(source.as< impl >()) +{ +} + +//! The method pushes the scope to the stack +void named_scope::push_scope(scope_entry const& entry) BOOST_NOEXCEPT +{ + impl::scope_list& s = impl::instance->get_scope_list(); + s.push_back(entry); +} + +//! The method pops the top scope +void named_scope::pop_scope() BOOST_NOEXCEPT +{ + impl::scope_list& s = impl::instance->get_scope_list(); + s.pop_back(); +} + +//! Returns the current thread's scope stack +named_scope::value_type const& named_scope::get_scopes() +{ + return impl::instance->get_scope_list(); +} + +} // namespace attributes + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/named_scope_format_parser.cpp b/src/boost/libs/log/src/named_scope_format_parser.cpp new file mode 100644 index 00000000..b7a602a7 --- /dev/null +++ b/src/boost/libs/log/src/named_scope_format_parser.cpp @@ -0,0 +1,746 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 named_scope_format_parser.cpp + * \author Andrey Semashev + * \date 14.11.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <cstring> +#include <string> +#include <vector> +#include <limits> +#include <algorithm> +#include <boost/cstdint.hpp> +#include <boost/move/core.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/spirit/include/karma_uint.hpp> +#include <boost/spirit/include/karma_generate.hpp> +#include <boost/log/attributes/named_scope.hpp> +#include <boost/log/expressions/formatters/named_scope.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/detail/header.hpp> + +namespace karma = boost::spirit::karma; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace expressions { + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! The function skips any spaces from the current position +BOOST_FORCEINLINE const char* skip_spaces(const char* p, const char* end) +{ + while (p < end && *p == ' ') + ++p; + return p; +} + +//! The function checks if the given character can be part of a function/type/namespace name +BOOST_FORCEINLINE bool is_name_character(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z'); +} + +//! The function checks if there is 'operator' keyword at the specified position +BOOST_FORCEINLINE bool is_operator_keyword(const char* p) +{ + return std::memcmp(p, "operator", 8) == 0; +} + +//! The function tries to parse operator signature +bool detect_operator(const char* begin, const char* end, const char* operator_keyword, const char*& operator_end) +{ + if (end - operator_keyword < 9 || !is_operator_keyword(operator_keyword)) + return false; + // Check that it's not a function name ending with 'operator', like detect_operator + if (operator_keyword > begin && is_name_character(*(operator_keyword - 1))) + return false; + + const char* p = skip_spaces(operator_keyword + 8, end); + if (p == end) + return false; + + // Check to see where the operator token ends + switch (*p) + { + case '(': + // Handle operator() + p = skip_spaces(++p, end); + if (p < end && *p == ')') + { + operator_end = p + 1; + return true; + } + + return false; + + case '[': + // Handle operator[] + p = skip_spaces(++p, end); + if (p < end && *p == ']') + { + operator_end = p + 1; + return true; + } + + return false; + + case '>': + case '<': + // Handle operator<=, operator>=, operator<<, operator>>, operator<<=, operator>>= + if (end - p >= 3 && (p[0] == p[1] && p[2] == '=')) + operator_end = p + 3; + else if (end - p >= 2 && (p[0] == p[1] || p[1] == '=')) + operator_end = p + 2; + else + operator_end = p + 1; + + return true; + + case '-': + // Handle operator->, operator->* + if (end - p >= 2 && p[1] == '>') + { + if (end - p >= 3 && p[2] == '*') + operator_end = p + 3; + else + operator_end = p + 2; + + return true; + } + // Fall through to other cases involving '-' + + case '=': + case '|': + case '&': + case '+': + // Handle operator=, operator==, operator+=, operator++, operator||, opeartor&&, etc. + if (end - p >= 2 && (p[0] == p[1] || p[1] == '=')) + operator_end = p + 2; + else + operator_end = p + 1; + + return true; + + case '*': + case '/': + case '%': + case '^': + // Handle operator*, operator*=, etc. + if (end - p >= 2 && p[1] == '=') + operator_end = p + 2; + else + operator_end = p + 1; + + return true; + + case ',': + case '~': + case '!': + // Handle operator,, operator~, etc. + operator_end = p + 1; + return true; + + case '"': + // Handle operator"" + if (end - p >= 2 && p[0] == p[1]) + { + p = skip_spaces(p + 2, end); + // Skip through the literal suffix + while (p < end && is_name_character(*p)) + ++p; + operator_end = p; + return true; + } + + return false; + + default: + // Handle type conversion operators. We can't find the end of the type reliably here. + operator_end = p; + return true; + } +} + +//! The function skips all template parameters +inline const char* skip_template_parameters(const char* begin, const char* end) +{ + unsigned int depth = 1; + const char* p = begin; + while (depth > 0 && p != end) + { + switch (*p) + { + case '>': + --depth; + break; + + case '<': + ++depth; + break; + + case 'o': + { + // Skip operators (e.g. when an operator is a non-type template parameter) + const char* operator_end; + if (detect_operator(begin, end, p, operator_end)) + { + p = operator_end; + continue; + } + } + break; + + default: + break; + } + + ++p; + } + + return p; +} + +//! The function seeks for the opening parenthesis and also tries to find the function name beginning +inline const char* find_opening_parenthesis(const char* begin, const char* end, const char*& first_name_begin, const char*& last_name_begin) +{ + enum sequence_state + { + not_started, // no significant (non-space) characters have been encountered so far + started, // some name has started; the name is a contiguous sequence of characters that may constitute a function or scope name + continued, // the previous characters were the scope operator ("::"), so the name is not finished yet + ended, // the name has ended; in particular, this means that there were significant characters previously in the string + operator_detected // operator has been found in the string, don't parse for scopes anymore; this is needed for conversion operators + }; + sequence_state state = not_started; + + const char* p = begin; + while (p != end) + { + char c = *p; + switch (c) + { + case '(': + if (state == not_started) + { + // If the opening brace is the first meaningful character in the string then this can't be a function signature. + // Pretend we didn't find the paranthesis to fail the parsing process. + return end; + } + return p; + + case '<': + if (state == not_started) + { + // Template parameters cannot start as the first meaningful character in the signature. + // Pretend we didn't find the paranthesis to fail the parsing process. + return end; + } + p = skip_template_parameters(p + 1, end); + if (state != operator_detected) + state = ended; + continue; + + case ' ': + if (state == started) + state = ended; + break; + + case ':': + ++p; + if (p != end && *p == ':') + { + if (state == not_started) + { + // Include the starting "::" in the full name + first_name_begin = p - 1; + } + if (state != operator_detected) + state = continued; + ++p; + } + else if (state != operator_detected) + { + // Weird case, a single colon. Maybe, some compilers would put things like "public:" in front of the signature. + state = ended; + } + continue; + + case 'o': + { + const char* operator_end; + if (detect_operator(begin, end, p, operator_end)) + { + if (state == not_started || state == ended) + first_name_begin = p; + last_name_begin = p; + p = operator_end; + state = operator_detected; + continue; + } + } + // Fall through to process this character as other characters + + default: + if (state != operator_detected) + { + if (is_name_character(c) || c == '~') // check for '~' in case of a destructor + { + if (state != started) + { + if (state == not_started || state == ended) + first_name_begin = p; + last_name_begin = p; + state = started; + } + } + else + { + state = ended; + } + } + break; + } + + ++p; + } + + return p; +} + +//! The function seeks for the closing parenthesis +inline const char* find_closing_parenthesis(const char* begin, const char* end, char& first_char) +{ + bool found_first_meaningful_char = false; + unsigned int depth = 1; + const char* p = begin; + while (p != end) + { + char c = *p; + switch (c) + { + case ')': + --depth; + if (depth == 0) + return p; + break; + + case '(': + ++depth; + break; + + case '<': + p = skip_template_parameters(p + 1, end); + continue; + + case 'o': + { + const char* operator_end; + if (detect_operator(begin, end, p, operator_end)) + { + p = operator_end; + continue; + } + } + // Fall through to process this character as other characters + + default: + if (!found_first_meaningful_char && c != ' ') + { + found_first_meaningful_char = true; + first_char = c; + } + break; + } + + ++p; + } + + return p; +} + +bool parse_function_name(const char*& begin, const char*& end, bool include_scope) +{ + // The algorithm tries to match several patterns to recognize function signatures. The most obvious is: + // + // A B(C) + // + // or just: + // + // B(C) + // + // in case of constructors, destructors and type conversion operators. The algorithm looks for the opening parenthesis and while doing that + // it detects the beginning of B. As a result B is the function name. + // + // The first significant complication is function and array return types, in which case the syntax becomes nested: + // + // A (*B(C))(D) + // A (&B(C))[D] + // + // In addition to that MSVC adds calling convention, such as __cdecl, to function types. In order to detect these cases the algorithm + // seeks for the closing parenthesis after the opening one. If there is an opening parenthesis or square bracket after the closing parenthesis + // then this is a function or array return type. The case of arrays is additionally complicated by GCC output: + // + // A B(C) [D] + // + // where D is template parameters description and is not part of the signature. To discern this special case from the array return type, the algorithm + // checks for the first significant character within the parenthesis. This character is '&' in case of arrays and something else otherwise. + // + // Speaking of template parameters, the parsing algorithm ignores them completely, assuming they are part of the name being parsed. This includes + // any possible parenthesis, nested template parameters and even operators, which may be present there as non-type template parameters. + // + // Operators pose another problem. This is especially the case for type conversion operators, and even more so for conversion operators to + // function types. In this latter case at least MSVC is known to produce incomprehensible strings which we cannot parse. In other cases it is + // too difficult to parse the type correctly. So we cheat a little. Whenever we find "operator", we know that we've found the function name + // already, and the name ends at the opening parenthesis. For other operators we are able to parse them correctly but that doesn't really matter. + // + // Note that the algorithm should be tolerant to different flavors of the input strings from different compilers, so we can't rely on spaces + // delimiting function names and other elements. Also, the algorithm should behave well in case of the fallback string generated by + // BOOST_CURRENT_FUNCTION (which is "(unknown)" currently). In case of any parsing failure the algorithm should return false, in which case the + // full original string will be used as the output. + + const char* b = begin; + const char* e = end; + while (b != e) + { + // Find the opening parenthesis. While looking for it, also find the function name. + // first_name_begin is the beginning of the function scope, last_name_begin is the actual function name. + const char* first_name_begin = NULL, *last_name_begin = NULL; + const char* paren_open = find_opening_parenthesis(b, e, first_name_begin, last_name_begin); + if (paren_open == e) + return false; + // Find the closing parenthesis. Also peek at the first character in the parenthesis, which we'll use to detect array return types. + char first_char_in_parenthesis = 0; + const char* paren_close = find_closing_parenthesis(paren_open + 1, e, first_char_in_parenthesis); + if (paren_close == e) + return false; + + const char* p = skip_spaces(paren_close + 1, e); + + // Detect function and array return types + if (p < e && (*p == '(' || (*p == '[' && first_char_in_parenthesis == '&'))) + { + // This is a function or array return type, the actual function name is within the parenthesis. + // Re-parse the string within the parenthesis as a function signature. + b = paren_open + 1; + e = paren_close; + continue; + } + + // We found something that looks like a function signature + if (include_scope) + { + if (!first_name_begin) + return false; + + begin = first_name_begin; + } + else + { + if (!last_name_begin) + return false; + + begin = last_name_begin; + } + + end = paren_open; + + return true; + } + + return false; +} + +template< typename CharT > +class named_scope_formatter +{ + BOOST_COPYABLE_AND_MOVABLE_ALT(named_scope_formatter) + +public: + typedef void result_type; + + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef basic_formatting_ostream< char_type > stream_type; + typedef attributes::named_scope::value_type::value_type value_type; + + struct literal + { + typedef void result_type; + + explicit literal(string_type& lit) { m_literal.swap(lit); } + + result_type operator() (stream_type& strm, value_type const&) const + { + strm << m_literal; + } + + private: + string_type m_literal; + }; + + struct scope_name + { + typedef void result_type; + + result_type operator() (stream_type& strm, value_type const& value) const + { + strm << value.scope_name; + } + }; + + struct function_name + { + typedef void result_type; + + explicit function_name(bool include_scope) : m_include_scope(include_scope) + { + } + + result_type operator() (stream_type& strm, value_type const& value) const + { + if (value.type == attributes::named_scope_entry::function) + { + const char* begin = value.scope_name.c_str(); + const char* end = begin + value.scope_name.size(); + if (parse_function_name(begin, end, m_include_scope)) + { + strm.write(begin, end - begin); + return; + } + } + + strm << value.scope_name; + } + + private: + const bool m_include_scope; + }; + + struct full_file_name + { + typedef void result_type; + + result_type operator() (stream_type& strm, value_type const& value) const + { + strm << value.file_name; + } + }; + + struct file_name + { + typedef void result_type; + + result_type operator() (stream_type& strm, value_type const& value) const + { + std::size_t n = value.file_name.size(), i = n; + for (; i > 0; --i) + { + const char c = value.file_name[i - 1]; +#if defined(BOOST_WINDOWS) + if (c == '\\') + break; +#endif + if (c == '/') + break; + } + strm.write(value.file_name.c_str() + i, n - i); + } + }; + + struct line_number + { + typedef void result_type; + + result_type operator() (stream_type& strm, value_type const& value) const + { + strm.flush(); + + char_type buf[std::numeric_limits< unsigned int >::digits10 + 2]; + char_type* p = buf; + + typedef karma::uint_generator< unsigned int, 10 > uint_gen; + karma::generate(p, uint_gen(), value.line); + + typedef typename stream_type::streambuf_type streambuf_type; + static_cast< streambuf_type* >(strm.rdbuf())->append(buf, static_cast< std::size_t >(p - buf)); + } + }; + +private: + typedef boost::log::aux::light_function< void (stream_type&, value_type const&) > formatter_type; + typedef std::vector< formatter_type > formatters; + +private: + formatters m_formatters; + +public: + BOOST_DEFAULTED_FUNCTION(named_scope_formatter(), {}) + named_scope_formatter(named_scope_formatter const& that) : m_formatters(that.m_formatters) {} + named_scope_formatter(BOOST_RV_REF(named_scope_formatter) that) { m_formatters.swap(that.m_formatters); } + + named_scope_formatter& operator= (named_scope_formatter that) + { + this->swap(that); + return *this; + } + + result_type operator() (stream_type& strm, value_type const& value) const + { + for (typename formatters::const_iterator it = m_formatters.begin(), end = m_formatters.end(); strm.good() && it != end; ++it) + { + (*it)(strm, value); + } + } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template< typename FunT > + void add_formatter(FunT&& fun) + { + m_formatters.emplace_back(boost::forward< FunT >(fun)); + } +#else + template< typename FunT > + void add_formatter(FunT const& fun) + { + m_formatters.push_back(formatter_type(fun)); + } +#endif + + void swap(named_scope_formatter& that) + { + m_formatters.swap(that.m_formatters); + } +}; + +//! Parses the named scope format string and constructs the formatter function +template< typename CharT > +BOOST_FORCEINLINE boost::log::aux::light_function< void (basic_formatting_ostream< CharT >&, attributes::named_scope::value_type::value_type const&) > +do_parse_named_scope_format(const CharT* begin, const CharT* end) +{ + typedef CharT char_type; + typedef boost::log::aux::light_function< void (basic_formatting_ostream< char_type >&, attributes::named_scope::value_type::value_type const&) > result_type; + typedef named_scope_formatter< char_type > formatter_type; + formatter_type fmt; + + std::basic_string< char_type > literal; + + while (begin != end) + { + const char_type* p = std::find(begin, end, static_cast< char_type >('%')); + literal.append(begin, p); + + if ((end - p) >= 2) + { + switch (p[1]) + { + case '%': + literal.push_back(static_cast< char_type >('%')); + break; + + case 'n': + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + fmt.add_formatter(typename formatter_type::scope_name()); + break; + + case 'c': + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + fmt.add_formatter(typename formatter_type::function_name(true)); + break; + + case 'C': + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + fmt.add_formatter(typename formatter_type::function_name(false)); + break; + + case 'f': + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + fmt.add_formatter(typename formatter_type::full_file_name()); + break; + + case 'F': + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + fmt.add_formatter(typename formatter_type::file_name()); + break; + + case 'l': + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + fmt.add_formatter(typename formatter_type::line_number()); + break; + + default: + literal.append(p, p + 2); + break; + } + + begin = p + 2; + } + else + { + if (p != end) + literal.push_back(static_cast< char_type >('%')); // a single '%' character at the end of the string + begin = end; + } + } + + if (!literal.empty()) + fmt.add_formatter(typename formatter_type::literal(literal)); + + return result_type(boost::move(fmt)); +} + +} // namespace + + +#ifdef BOOST_LOG_USE_CHAR + +//! Parses the named scope format string and constructs the formatter function +BOOST_LOG_API boost::log::aux::light_function< void (basic_formatting_ostream< char >&, attributes::named_scope::value_type::value_type const&) > +parse_named_scope_format(const char* begin, const char* end) +{ + return do_parse_named_scope_format(begin, end); +} + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +//! Parses the named scope format string and constructs the formatter function +BOOST_LOG_API boost::log::aux::light_function< void (basic_formatting_ostream< wchar_t >&, attributes::named_scope::value_type::value_type const&) > +parse_named_scope_format(const wchar_t* begin, const wchar_t* end) +{ + return do_parse_named_scope_format(begin, end); +} + +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace aux + +} // namespace expressions + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/once_block.cpp b/src/boost/libs/log/src/once_block.cpp new file mode 100644 index 00000000..c76009da --- /dev/null +++ b/src/boost/libs/log/src/once_block.cpp @@ -0,0 +1,471 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 once_block.cpp + * \author Andrey Semashev + * \date 23.06.2010 + * + * \brief This file is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * The code in this file is based on the \c call_once function implementation in Boost.Thread. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/log/utility/once_block.hpp> +#ifndef BOOST_LOG_NO_THREADS + +#include <cstdlib> +#include <boost/assert.hpp> + +#if defined(BOOST_THREAD_PLATFORM_WIN32) + +#include <boost/winapi/wait.hpp> // INFINITE + +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +#include <boost/winapi/srw_lock.hpp> +#include <boost/winapi/condition_variable.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +boost::winapi::SRWLOCK_ g_OnceBlockMutex = BOOST_WINAPI_SRWLOCK_INIT; +boost::winapi::CONDITION_VARIABLE_ g_OnceBlockCond = BOOST_WINAPI_CONDITION_VARIABLE_INIT; + +} // namespace + +BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT +{ + boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); + + once_block_flag volatile& flag = m_flag; + while (flag.status != once_block_flag::initialized) + { + if (flag.status == once_block_flag::uninitialized) + { + flag.status = once_block_flag::being_initialized; + boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); + + // Invoke the initializer block + return false; + } + else + { + while (flag.status == once_block_flag::being_initialized) + { + BOOST_VERIFY(boost::winapi::SleepConditionVariableSRW( + &g_OnceBlockCond, &g_OnceBlockMutex, boost::winapi::INFINITE_, 0)); + } + } + } + + boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); + + return true; +} + +BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT +{ + boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); + + // The initializer executed successfully + m_flag.status = once_block_flag::initialized; + + boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); + boost::winapi::WakeAllConditionVariable(&g_OnceBlockCond); +} + +BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT +{ + boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); + + // The initializer failed, marking the flag as if it hasn't run at all + m_flag.status = once_block_flag::uninitialized; + + boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); + boost::winapi::WakeAllConditionVariable(&g_OnceBlockCond); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +#include <cstdlib> // atexit +#include <boost/detail/interlocked.hpp> +#include <boost/winapi/basic_types.hpp> +#include <boost/winapi/dll.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/locks.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + struct BOOST_LOG_NO_VTABLE once_block_impl_base + { + virtual ~once_block_impl_base() {} + virtual bool enter_once_block(once_block_flag volatile& flag) = 0; + virtual void commit(once_block_flag& flag) = 0; + virtual void rollback(once_block_flag& flag) = 0; + }; + + class once_block_impl_nt6 : + public once_block_impl_base + { + public: + struct winapi_srwlock { void* p; }; + struct winapi_condition_variable { void* p; }; + + typedef void (BOOST_WINAPI_WINAPI_CC *InitializeSRWLock_t)(winapi_srwlock*); + typedef void (BOOST_WINAPI_WINAPI_CC *AcquireSRWLockExclusive_t)(winapi_srwlock*); + typedef void (BOOST_WINAPI_WINAPI_CC *ReleaseSRWLockExclusive_t)(winapi_srwlock*); + typedef void (BOOST_WINAPI_WINAPI_CC *InitializeConditionVariable_t)(winapi_condition_variable*); + typedef boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *SleepConditionVariableSRW_t)(winapi_condition_variable*, winapi_srwlock*, boost::winapi::DWORD_, boost::winapi::ULONG_); + typedef void (BOOST_WINAPI_WINAPI_CC *WakeAllConditionVariable_t)(winapi_condition_variable*); + + private: + winapi_srwlock m_Mutex; + winapi_condition_variable m_Cond; + + AcquireSRWLockExclusive_t m_pAcquireSRWLockExclusive; + ReleaseSRWLockExclusive_t m_pReleaseSRWLockExclusive; + SleepConditionVariableSRW_t m_pSleepConditionVariableSRW; + WakeAllConditionVariable_t m_pWakeAllConditionVariable; + + public: + once_block_impl_nt6( + InitializeSRWLock_t pInitializeSRWLock, + AcquireSRWLockExclusive_t pAcquireSRWLockExclusive, + ReleaseSRWLockExclusive_t pReleaseSRWLockExclusive, + InitializeConditionVariable_t pInitializeConditionVariable, + SleepConditionVariableSRW_t pSleepConditionVariableSRW, + WakeAllConditionVariable_t pWakeAllConditionVariable + ) : + m_pAcquireSRWLockExclusive(pAcquireSRWLockExclusive), + m_pReleaseSRWLockExclusive(pReleaseSRWLockExclusive), + m_pSleepConditionVariableSRW(pSleepConditionVariableSRW), + m_pWakeAllConditionVariable(pWakeAllConditionVariable) + { + pInitializeSRWLock(&m_Mutex); + pInitializeConditionVariable(&m_Cond); + } + + bool enter_once_block(once_block_flag volatile& flag) + { + m_pAcquireSRWLockExclusive(&m_Mutex); + + while (flag.status != once_block_flag::initialized) + { + if (flag.status == once_block_flag::uninitialized) + { + flag.status = once_block_flag::being_initialized; + m_pReleaseSRWLockExclusive(&m_Mutex); + + // Invoke the initializer block + return false; + } + else + { + while (flag.status == once_block_flag::being_initialized) + { + BOOST_VERIFY(m_pSleepConditionVariableSRW( + &m_Cond, &m_Mutex, boost::winapi::INFINITE_, 0)); + } + } + } + + m_pReleaseSRWLockExclusive(&m_Mutex); + + return true; + } + + void commit(once_block_flag& flag) + { + m_pAcquireSRWLockExclusive(&m_Mutex); + + // The initializer executed successfully + flag.status = once_block_flag::initialized; + + m_pReleaseSRWLockExclusive(&m_Mutex); + m_pWakeAllConditionVariable(&m_Cond); + } + + void rollback(once_block_flag& flag) + { + m_pAcquireSRWLockExclusive(&m_Mutex); + + // The initializer failed, marking the flag as if it hasn't run at all + flag.status = once_block_flag::uninitialized; + + m_pReleaseSRWLockExclusive(&m_Mutex); + m_pWakeAllConditionVariable(&m_Cond); + } + }; + + class once_block_impl_nt5 : + public once_block_impl_base + { + private: + mutex m_Mutex; + condition_variable m_Cond; + + public: + bool enter_once_block(once_block_flag volatile& flag) + { + unique_lock< mutex > lock(m_Mutex); + + while (flag.status != once_block_flag::initialized) + { + if (flag.status == once_block_flag::uninitialized) + { + flag.status = once_block_flag::being_initialized; + + // Invoke the initializer block + return false; + } + else + { + while (flag.status == once_block_flag::being_initialized) + { + m_Cond.wait(lock); + } + } + } + + return true; + } + + void commit(once_block_flag& flag) + { + { + lock_guard< mutex > lock(m_Mutex); + flag.status = once_block_flag::initialized; + } + m_Cond.notify_all(); + } + + void rollback(once_block_flag& flag) + { + { + lock_guard< mutex > lock(m_Mutex); + flag.status = once_block_flag::uninitialized; + } + m_Cond.notify_all(); + } + }; + + once_block_impl_base* create_once_block_impl() + { + boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); + if (hKernel32) + { + once_block_impl_nt6::InitializeSRWLock_t pInitializeSRWLock = + (once_block_impl_nt6::InitializeSRWLock_t)boost::winapi::get_proc_address(hKernel32, "InitializeSRWLock"); + if (pInitializeSRWLock) + { + once_block_impl_nt6::AcquireSRWLockExclusive_t pAcquireSRWLockExclusive = + (once_block_impl_nt6::AcquireSRWLockExclusive_t)boost::winapi::get_proc_address(hKernel32, "AcquireSRWLockExclusive"); + if (pAcquireSRWLockExclusive) + { + once_block_impl_nt6::ReleaseSRWLockExclusive_t pReleaseSRWLockExclusive = + (once_block_impl_nt6::ReleaseSRWLockExclusive_t)boost::winapi::get_proc_address(hKernel32, "ReleaseSRWLockExclusive"); + if (pReleaseSRWLockExclusive) + { + once_block_impl_nt6::InitializeConditionVariable_t pInitializeConditionVariable = + (once_block_impl_nt6::InitializeConditionVariable_t)boost::winapi::get_proc_address(hKernel32, "InitializeConditionVariable"); + if (pInitializeConditionVariable) + { + once_block_impl_nt6::SleepConditionVariableSRW_t pSleepConditionVariableSRW = + (once_block_impl_nt6::SleepConditionVariableSRW_t)boost::winapi::get_proc_address(hKernel32, "SleepConditionVariableSRW"); + if (pSleepConditionVariableSRW) + { + once_block_impl_nt6::WakeAllConditionVariable_t pWakeAllConditionVariable = + (once_block_impl_nt6::WakeAllConditionVariable_t)boost::winapi::get_proc_address(hKernel32, "WakeAllConditionVariable"); + if (pWakeAllConditionVariable) + { + return new once_block_impl_nt6( + pInitializeSRWLock, + pAcquireSRWLockExclusive, + pReleaseSRWLockExclusive, + pInitializeConditionVariable, + pSleepConditionVariableSRW, + pWakeAllConditionVariable); + } + } + } + } + } + } + } + + return new once_block_impl_nt5(); + } + + once_block_impl_base* g_pOnceBlockImpl = NULL; + + void destroy_once_block_impl() + { + once_block_impl_base* impl = (once_block_impl_base*) + BOOST_INTERLOCKED_EXCHANGE_POINTER((void**)&g_pOnceBlockImpl, NULL); + delete impl; + } + + once_block_impl_base* get_once_block_impl() BOOST_NOEXCEPT + { + once_block_impl_base* impl = g_pOnceBlockImpl; + if (!impl) try + { + once_block_impl_base* new_impl = create_once_block_impl(); + impl = (once_block_impl_base*) + BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER((void**)&g_pOnceBlockImpl, (void*)new_impl, NULL); + if (impl) + { + delete new_impl; + } + else + { + std::atexit(&destroy_once_block_impl); + return new_impl; + } + } + catch (...) + { + BOOST_ASSERT_MSG(false, "Boost.Log: Failed to initialize the once block thread synchronization structures"); + std::abort(); + } + + return impl; + } + +} // namespace + +BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT +{ + return get_once_block_impl()->enter_once_block(m_flag); +} + +BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT +{ + get_once_block_impl()->commit(m_flag); +} + +BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT +{ + get_once_block_impl()->rollback(m_flag); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) + +#include <pthread.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +static pthread_mutex_t g_OnceBlockMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t g_OnceBlockCond = PTHREAD_COND_INITIALIZER; + +} // namespace + +BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT +{ + BOOST_VERIFY(!pthread_mutex_lock(&g_OnceBlockMutex)); + + once_block_flag volatile& flag = m_flag; + while (flag.status != once_block_flag::initialized) + { + if (flag.status == once_block_flag::uninitialized) + { + flag.status = once_block_flag::being_initialized; + BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); + + // Invoke the initializer block + return false; + } + else + { + while (flag.status == once_block_flag::being_initialized) + { + BOOST_VERIFY(!pthread_cond_wait(&g_OnceBlockCond, &g_OnceBlockMutex)); + } + } + } + + BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); + + return true; +} + +BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT +{ + BOOST_VERIFY(!pthread_mutex_lock(&g_OnceBlockMutex)); + + // The initializer executed successfully + m_flag.status = once_block_flag::initialized; + + BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); + BOOST_VERIFY(!pthread_cond_broadcast(&g_OnceBlockCond)); +} + +BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT +{ + BOOST_VERIFY(!pthread_mutex_lock(&g_OnceBlockMutex)); + + // The initializer failed, marking the flag as if it hasn't run at all + m_flag.status = once_block_flag::uninitialized; + + BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); + BOOST_VERIFY(!pthread_cond_broadcast(&g_OnceBlockCond)); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#else +#error Boost.Log: unsupported threading API +#endif + +#endif // BOOST_LOG_NO_THREADS diff --git a/src/boost/libs/log/src/permissions.cpp b/src/boost/libs/log/src/permissions.cpp new file mode 100644 index 00000000..9bf80964 --- /dev/null +++ b/src/boost/libs/log/src/permissions.cpp @@ -0,0 +1,94 @@ +/* + * Copyright Andrey Semashev 2015. + * 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 permissions.cpp + * \author Andrey Semashev + * \date 26.12.2015 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/log/utility/permissions.hpp> +#include <boost/interprocess/permissions.hpp> + +#if defined(BOOST_WINDOWS) + +#include <boost/log/exceptions.hpp> +#include <boost/throw_exception.hpp> +#include <boost/log/utility/once_block.hpp> +#include <windows.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +static ::SECURITY_DESCRIPTOR g_unrestricted_security_descriptor; +static ::SECURITY_ATTRIBUTES g_unrestricted_security_attributes; + +} // namespace + +BOOST_LOG_API permissions::native_type permissions::get_unrestricted_security_attributes() +{ + BOOST_LOG_ONCE_BLOCK() + { + if (!InitializeSecurityDescriptor(&g_unrestricted_security_descriptor, SECURITY_DESCRIPTOR_REVISION)) + { + DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to initialize security descriptor", (err)); + } + + if (!SetSecurityDescriptorDacl(&g_unrestricted_security_descriptor, TRUE, NULL, FALSE)) + { + DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to set null DACL to a security descriptor", (err)); + } + + g_unrestricted_security_attributes.nLength = sizeof(g_unrestricted_security_attributes); + g_unrestricted_security_attributes.lpSecurityDescriptor = &g_unrestricted_security_descriptor; + g_unrestricted_security_attributes.bInheritHandle = FALSE; + } + + return &g_unrestricted_security_attributes; +} + +//! Initializing constructor +BOOST_LOG_API permissions::permissions(boost::interprocess::permissions const& perms) BOOST_NOEXCEPT : + m_perms(reinterpret_cast< native_type >(perms.get_permissions())) +{ +} + + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#else // defined(BOOST_WINDOWS) + +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +//! Initializing constructor +BOOST_LOG_API permissions::permissions(boost::interprocess::permissions const& perms) BOOST_NOEXCEPT : + m_perms(static_cast< native_type >(perms.get_permissions())) +{ +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/posix/ipc_reliable_message_queue.cpp b/src/boost/libs/log/src/posix/ipc_reliable_message_queue.cpp new file mode 100644 index 00000000..07d61fa5 --- /dev/null +++ b/src/boost/libs/log/src/posix/ipc_reliable_message_queue.cpp @@ -0,0 +1,881 @@ +/* + * Copyright Lingxi Li 2015. + * Copyright Andrey Semashev 2016. + * 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 posix/ipc_reliable_message_queue.cpp + * \author Lingxi Li + * \author Andrey Semashev + * \date 17.11.2015 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * This file provides an interprocess message queue implementation on POSIX platforms. + */ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <cerrno> +#include <cstring> +#include <new> +#include <string> +#include <stdexcept> +#include <algorithm> +#include <unistd.h> +#if defined(BOOST_HAS_SCHED_YIELD) +#include <sched.h> +#elif defined(BOOST_HAS_PTHREAD_YIELD) +#include <pthread.h> +#elif defined(BOOST_HAS_NANOSLEEP) +#include <time.h> +#endif +#include <boost/assert.hpp> +#include <boost/static_assert.hpp> +#include <boost/cstdint.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/atomic/capabilities.hpp> +#include <boost/throw_exception.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/ipc/reliable_message_queue.hpp> +#include <boost/log/support/exception.hpp> +#include <boost/log/detail/pause.hpp> +#include <boost/exception/info.hpp> +#include <boost/exception/enable_error_info.hpp> +#include <boost/interprocess/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/permissions.hpp> +#include <boost/interprocess/mapped_region.hpp> +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/align/align_up.hpp> +#include "ipc_sync_wrappers.hpp" +#include "murmur3.hpp" +#include "bit_tools.hpp" +#include <boost/log/detail/header.hpp> + +#if BOOST_ATOMIC_INT32_LOCK_FREE != 2 +// 32-bit atomic ops are required to be able to place atomic<uint32_t> in the process-shared memory +#error Boost.Log: Native 32-bit atomic operations are required but not supported by Boost.Atomic on the target platform +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +//! Message queue implementation data +struct reliable_message_queue::implementation +{ +private: + //! Header of an allocation block within the message queue. Placed at the beginning of the block within the shared memory segment. + struct block_header + { + // Element data alignment, in bytes + enum { data_alignment = 32u }; + + //! Size of the element data, in bytes + size_type m_size; + + //! Returns the block header overhead, in bytes + static BOOST_CONSTEXPR size_type get_header_overhead() BOOST_NOEXCEPT + { + return static_cast< size_type >(boost::alignment::align_up(sizeof(block_header), data_alignment)); + } + + //! Returns a pointer to the element data + void* get_data() const BOOST_NOEXCEPT + { + return const_cast< unsigned char* >(reinterpret_cast< const unsigned char* >(this)) + get_header_overhead(); + } + }; + + //! Header of the message queue. Placed at the beginning of the shared memory segment. + struct header + { + // Increment this constant whenever you change the binary layout of the queue (apart from this header structure) + enum { abi_version = 0 }; + + // !!! Whenever you add/remove members in this structure, also modify get_abi_tag() function accordingly !!! + + //! A tag value to ensure the correct binary layout of the message queue data structures. Must be placed first and always have a fixed size and alignment. + uint32_t m_abi_tag; + //! Padding to protect against alignment changes in Boost.Atomic. Don't use BOOST_ALIGNMENT to ensure portability. + unsigned char m_padding[BOOST_LOG_CPU_CACHE_LINE_SIZE - sizeof(uint32_t)]; + //! Reference counter. Also acts as a flag indicating that the queue is constructed (i.e. the queue is constructed when the counter is not 0). + boost::atomic< uint32_t > m_ref_count; + //! Number of allocation blocks in the queue. + const uint32_t m_capacity; + //! Size of an allocation block, in bytes. + const size_type m_block_size; + //! Mutex for protecting queue data structures. + boost::log::ipc::aux::interprocess_mutex m_mutex; + //! Condition variable used to block readers when the queue is empty. + boost::log::ipc::aux::interprocess_condition_variable m_nonempty_queue; + //! Condition variable used to block writers when the queue is full. + boost::log::ipc::aux::interprocess_condition_variable m_nonfull_queue; + //! The current number of allocated blocks in the queue. + uint32_t m_size; + //! The current writing position (allocation block index). + uint32_t m_put_pos; + //! The current reading position (allocation block index). + uint32_t m_get_pos; + + header(uint32_t capacity, size_type block_size) : + m_abi_tag(get_abi_tag()), + m_capacity(capacity), + m_block_size(block_size), + m_size(0u), + m_put_pos(0u), + m_get_pos(0u) + { + // Must be initialized last. m_ref_count is zero-initialized initially. + m_ref_count.fetch_add(1u, boost::memory_order_release); + } + + //! Returns the header structure ABI tag + static uint32_t get_abi_tag() BOOST_NOEXCEPT + { + // This FOURCC identifies the queue type + boost::log::aux::murmur3_32 hash(boost::log::aux::make_fourcc('r', 'e', 'l', 'q')); + + // This FOURCC identifies the queue implementation + hash.mix(boost::log::aux::make_fourcc('p', 't', 'h', 'r')); + hash.mix(abi_version); + + // We will use these constants to align pointers + hash.mix(BOOST_LOG_CPU_CACHE_LINE_SIZE); + hash.mix(block_header::data_alignment); + + // The members in the sequence below must be enumerated in the same order as they are declared in the header structure. + // The ABI tag is supposed change whenever a member changes size or offset from the beginning of the header. + +#define BOOST_LOG_MIX_HEADER_MEMBER(name)\ + hash.mix(static_cast< uint32_t >(sizeof(((header*)NULL)->name)));\ + hash.mix(static_cast< uint32_t >(offsetof(header, name))) + + BOOST_LOG_MIX_HEADER_MEMBER(m_abi_tag); + BOOST_LOG_MIX_HEADER_MEMBER(m_padding); + BOOST_LOG_MIX_HEADER_MEMBER(m_ref_count); + BOOST_LOG_MIX_HEADER_MEMBER(m_capacity); + BOOST_LOG_MIX_HEADER_MEMBER(m_block_size); + BOOST_LOG_MIX_HEADER_MEMBER(m_mutex); + BOOST_LOG_MIX_HEADER_MEMBER(m_nonempty_queue); + BOOST_LOG_MIX_HEADER_MEMBER(m_nonfull_queue); + BOOST_LOG_MIX_HEADER_MEMBER(m_size); + BOOST_LOG_MIX_HEADER_MEMBER(m_put_pos); + BOOST_LOG_MIX_HEADER_MEMBER(m_get_pos); + +#undef BOOST_LOG_MIX_HEADER_MEMBER + + return hash.finalize(); + } + + //! Returns an element header at the specified index + block_header* get_block(uint32_t index) const BOOST_NOEXCEPT + { + BOOST_ASSERT(index < m_capacity); + unsigned char* p = const_cast< unsigned char* >(reinterpret_cast< const unsigned char* >(this)) + boost::alignment::align_up(sizeof(header), BOOST_LOG_CPU_CACHE_LINE_SIZE); + p += static_cast< std::size_t >(m_block_size) * static_cast< std::size_t >(index); + return reinterpret_cast< block_header* >(p); + } + + BOOST_DELETED_FUNCTION(header(header const&)) + BOOST_DELETED_FUNCTION(header& operator=(header const&)) + }; + +private: + //! Shared memory object + boost::interprocess::shared_memory_object m_shared_memory; + //! Shared memory mapping into the process address space + boost::interprocess::mapped_region m_region; + //! Queue overflow handling policy + const overflow_policy m_overflow_policy; + //! The mask for selecting bits that constitute size values from 0 to (block_size - 1) + size_type m_block_size_mask; + //! The number of the bit set in block_size (i.e. log base 2 of block_size) + uint32_t m_block_size_log2; + //! The flag indicates that stop has been requested + bool m_stop; + + //! Queue shared memory object name + const object_name m_name; + +public: + //! The constructor creates a new shared memory segment + implementation + ( + open_mode::create_only_tag, + object_name const& name, + uint32_t capacity, + size_type block_size, + overflow_policy oflow_policy, + permissions const& perms + ) : + m_shared_memory(boost::interprocess::create_only, name.c_str(), boost::interprocess::read_write, boost::interprocess::permissions(perms.get_native())), + m_region(), + m_overflow_policy(oflow_policy), + m_block_size_mask(0u), + m_block_size_log2(0u), + m_stop(false), + m_name(name) + { + create_region(capacity, block_size); + } + + //! The constructor creates a new shared memory segment or opens the existing one + implementation + ( + open_mode::open_or_create_tag, + object_name const& name, + uint32_t capacity, + size_type block_size, + overflow_policy oflow_policy, + permissions const& perms + ) : + m_shared_memory(boost::interprocess::open_or_create, name.c_str(), boost::interprocess::read_write, boost::interprocess::permissions(perms.get_native())), + m_region(), + m_overflow_policy(oflow_policy), + m_block_size_mask(0u), + m_block_size_log2(0u), + m_stop(false), + m_name(name) + { + boost::interprocess::offset_t shmem_size = 0; + if (!m_shared_memory.get_size(shmem_size) || shmem_size == 0) + create_region(capacity, block_size); + else + adopt_region(shmem_size); + } + + //! The constructor opens the existing shared memory segment + implementation + ( + open_mode::open_only_tag, + object_name const& name, + overflow_policy oflow_policy + ) : + m_shared_memory(boost::interprocess::open_only, name.c_str(), boost::interprocess::read_write), + m_region(), + m_overflow_policy(oflow_policy), + m_block_size_mask(0u), + m_block_size_log2(0u), + m_stop(false), + m_name(name) + { + boost::interprocess::offset_t shmem_size = 0; + if (!m_shared_memory.get_size(shmem_size)) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment not found"); + + adopt_region(shmem_size); + } + + ~implementation() + { + close_region(); + } + + object_name const& name() const BOOST_NOEXCEPT + { + return m_name; + } + + uint32_t capacity() const BOOST_NOEXCEPT + { + return get_header()->m_capacity; + } + + size_type block_size() const BOOST_NOEXCEPT + { + return get_header()->m_block_size; + } + + operation_result send(void const* message_data, size_type message_size) + { + const uint32_t block_count = estimate_block_count(message_size); + + header* const hdr = get_header(); + + if (BOOST_UNLIKELY(block_count > hdr->m_capacity)) + BOOST_LOG_THROW_DESCR(logic_error, "Message size exceeds the interprocess queue capacity"); + + if (m_stop) + return aborted; + + lock_queue(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); + + while (true) + { + if (m_stop) + return aborted; + + if ((hdr->m_capacity - hdr->m_size) >= block_count) + break; + + const overflow_policy oflow_policy = m_overflow_policy; + if (oflow_policy == fail_on_overflow) + return no_space; + else if (BOOST_UNLIKELY(oflow_policy == throw_on_overflow)) + BOOST_LOG_THROW_DESCR(capacity_limit_reached, "Interprocess queue is full"); + + hdr->m_nonfull_queue.wait(hdr->m_mutex); + } + + enqueue_message(message_data, message_size, block_count); + + return succeeded; + } + + bool try_send(void const* message_data, size_type message_size) + { + const uint32_t block_count = estimate_block_count(message_size); + + header* const hdr = get_header(); + + if (BOOST_UNLIKELY(block_count > hdr->m_capacity)) + BOOST_LOG_THROW_DESCR(logic_error, "Message size exceeds the interprocess queue capacity"); + + if (m_stop) + return false; + + lock_queue(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); + + if (m_stop) + return false; + + if ((hdr->m_capacity - hdr->m_size) < block_count) + return false; + + enqueue_message(message_data, message_size, block_count); + + return true; + } + + operation_result receive(receive_handler handler, void* state) + { + if (m_stop) + return aborted; + + lock_queue(); + header* const hdr = get_header(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); + + while (true) + { + if (m_stop) + return aborted; + + if (hdr->m_size > 0u) + break; + + hdr->m_nonempty_queue.wait(hdr->m_mutex); + } + + dequeue_message(handler, state); + + return succeeded; + } + + bool try_receive(receive_handler handler, void* state) + { + if (m_stop) + return false; + + lock_queue(); + header* const hdr = get_header(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); + + if (hdr->m_size == 0u) + return false; + + dequeue_message(handler, state); + + return true; + } + + void stop_local() + { + if (m_stop) + return; + + lock_queue(); + header* const hdr = get_header(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); + + m_stop = true; + + hdr->m_nonempty_queue.notify_all(); + hdr->m_nonfull_queue.notify_all(); + } + + void reset_local() + { + m_stop = false; + } + + void clear() + { + lock_queue(); + header* const hdr = get_header(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); + clear_queue(); + } + +private: + header* get_header() const BOOST_NOEXCEPT + { + return static_cast< header* >(m_region.get_address()); + } + + static std::size_t estimate_region_size(uint32_t capacity, size_type block_size) BOOST_NOEXCEPT + { + return boost::alignment::align_up(sizeof(header), BOOST_LOG_CPU_CACHE_LINE_SIZE) + static_cast< std::size_t >(capacity) * static_cast< std::size_t >(block_size); + } + + void create_region(uint32_t capacity, size_type block_size) + { + const std::size_t shmem_size = estimate_region_size(capacity, block_size); + m_shared_memory.truncate(shmem_size); + boost::interprocess::mapped_region(m_shared_memory, boost::interprocess::read_write, 0u, shmem_size).swap(m_region); + + new (m_region.get_address()) header(capacity, block_size); + + init_block_size(block_size); + } + + void adopt_region(std::size_t shmem_size) + { + if (shmem_size < sizeof(header)) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment size too small"); + + boost::interprocess::mapped_region(m_shared_memory, boost::interprocess::read_write, 0u, shmem_size).swap(m_region); + + // Wait until the mapped region becomes initialized + header* const hdr = get_header(); + BOOST_CONSTEXPR_OR_CONST unsigned int wait_loops = 200u, spin_loops = 16u, spins = 16u; + for (unsigned int i = 0; i < wait_loops; ++i) + { + uint32_t ref_count = hdr->m_ref_count.load(boost::memory_order_acquire); + while (ref_count > 0u) + { + if (hdr->m_ref_count.compare_exchange_weak(ref_count, ref_count + 1u, boost::memory_order_acq_rel, boost::memory_order_acquire)) + goto done; + } + + if (i < spin_loops) + { + for (unsigned int j = 0; j < spins; ++j) + { + boost::log::aux::pause(); + } + } + else + { +#if defined(BOOST_HAS_SCHED_YIELD) + sched_yield(); +#elif defined(BOOST_HAS_PTHREAD_YIELD) + pthread_yield(); +#elif defined(BOOST_HAS_NANOSLEEP) + timespec ts = {}; + ts.tv_sec = 0; + ts.tv_nsec = 1000; + nanosleep(&ts, NULL); +#else + usleep(1); +#endif + } + } + + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment is not initialized by creator for too long"); + + done: + try + { + // Check that the queue layout matches the current process ABI + if (hdr->m_abi_tag != header::get_abi_tag()) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: the queue ABI is incompatible"); + + if (!boost::log::aux::is_power_of_2(hdr->m_block_size)) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: the queue block size is not a power of 2"); + + init_block_size(hdr->m_block_size); + } + catch (...) + { + close_region(); + throw; + } + } + + void close_region() BOOST_NOEXCEPT + { + header* const hdr = get_header(); + + if (hdr->m_ref_count.fetch_sub(1u, boost::memory_order_acq_rel) == 1u) + { + boost::interprocess::shared_memory_object::remove(m_shared_memory.get_name()); + + hdr->~header(); + + boost::interprocess::mapped_region().swap(m_region); + boost::interprocess::shared_memory_object().swap(m_shared_memory); + + m_block_size_mask = 0u; + m_block_size_log2 = 0u; + } + } + + void init_block_size(size_type block_size) + { + m_block_size_mask = block_size - 1u; + + uint32_t block_size_log2 = 0u; + if ((block_size & 0x0000ffff) == 0u) + { + block_size >>= 16u; + block_size_log2 += 16u; + } + if ((block_size & 0x000000ff) == 0u) + { + block_size >>= 8u; + block_size_log2 += 8u; + } + if ((block_size & 0x0000000f) == 0u) + { + block_size >>= 4u; + block_size_log2 += 4u; + } + if ((block_size & 0x00000003) == 0u) + { + block_size >>= 2u; + block_size_log2 += 2u; + } + if ((block_size & 0x00000001) == 0u) + { + ++block_size_log2; + } + m_block_size_log2 = block_size_log2; + } + + void lock_queue() + { + header* const hdr = get_header(); + +#if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) + try + { +#endif + hdr->m_mutex.lock(); +#if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) + } + catch (boost::log::ipc::aux::lock_owner_dead&) + { + // The mutex is locked by the current thread, but the previous owner terminated without releasing the lock + try + { + clear_queue(); + hdr->m_mutex.recover(); + } + catch (...) + { + hdr->m_mutex.unlock(); + throw; + } + } +#endif + } + + void clear_queue() + { + header* const hdr = get_header(); + hdr->m_size = 0u; + hdr->m_put_pos = 0u; + hdr->m_get_pos = 0u; + hdr->m_nonfull_queue.notify_all(); + } + + //! Returns the number of allocation blocks that are required to store user's payload of the specified size + uint32_t estimate_block_count(size_type size) const BOOST_NOEXCEPT + { + // ceil((size + get_header_overhead()) / block_size) + return static_cast< uint32_t >((size + block_header::get_header_overhead() + m_block_size_mask) >> m_block_size_log2); + } + + //! Puts the message to the back of the queue + void enqueue_message(void const* message_data, size_type message_size, uint32_t block_count) + { + header* const hdr = get_header(); + + const uint32_t capacity = hdr->m_capacity; + const size_type block_size = hdr->m_block_size; + uint32_t pos = hdr->m_put_pos; + + block_header* block = hdr->get_block(pos); + block->m_size = message_size; + + size_type write_size = (std::min)(static_cast< size_type >((capacity - pos) * block_size - block_header::get_header_overhead()), message_size); + std::memcpy(block->get_data(), message_data, write_size); + + pos += block_count; + if (BOOST_UNLIKELY(pos >= capacity)) + { + // Write the rest of the message at the beginning of the queue + pos -= capacity; + message_data = static_cast< const unsigned char* >(message_data) + write_size; + write_size = message_size - write_size; + if (write_size > 0u) + std::memcpy(hdr->get_block(0u), message_data, write_size); + } + + hdr->m_put_pos = pos; + + const uint32_t old_queue_size = hdr->m_size; + hdr->m_size = old_queue_size + block_count; + if (old_queue_size == 0u) + hdr->m_nonempty_queue.notify_one(); + } + + //! Retrieves the next message and invokes the handler to store the message contents + void dequeue_message(receive_handler handler, void* state) + { + header* const hdr = get_header(); + + const uint32_t capacity = hdr->m_capacity; + const size_type block_size = hdr->m_block_size; + uint32_t pos = hdr->m_get_pos; + + block_header* block = hdr->get_block(pos); + size_type message_size = block->m_size; + uint32_t block_count = estimate_block_count(message_size); + + BOOST_ASSERT(block_count <= hdr->m_size); + + size_type read_size = (std::min)(static_cast< size_type >((capacity - pos) * block_size - block_header::get_header_overhead()), message_size); + handler(state, block->get_data(), read_size); + + pos += block_count; + if (BOOST_UNLIKELY(pos >= capacity)) + { + // Read the tail of the message + pos -= capacity; + read_size = message_size - read_size; + if (read_size > 0u) + handler(state, hdr->get_block(0u), read_size); + } + + hdr->m_get_pos = pos; + hdr->m_size -= block_count; + + hdr->m_nonfull_queue.notify_all(); + } +}; + +BOOST_LOG_API void reliable_message_queue::create(object_name const& name, uint32_t capacity, size_type block_size, overflow_policy oflow_policy, permissions const& perms) +{ + BOOST_ASSERT(m_impl == NULL); + if (!boost::log::aux::is_power_of_2(block_size)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Interprocess message queue block size is not a power of 2")); + try + { + m_impl = new implementation(open_mode::create_only, name, capacity, static_cast< size_type >(boost::alignment::align_up(block_size, BOOST_LOG_CPU_CACHE_LINE_SIZE)), oflow_policy, perms); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(name); + throw; + } + catch (boost::interprocess::interprocess_exception& e) + { + BOOST_THROW_EXCEPTION(boost::enable_error_info(system_error(boost::system::error_code(e.get_native_error(), boost::system::system_category()), e.what())) << boost::log::ipc::object_name_info(name)); + } +} + +BOOST_LOG_API void reliable_message_queue::open_or_create(object_name const& name, uint32_t capacity, size_type block_size, overflow_policy oflow_policy, permissions const& perms) +{ + BOOST_ASSERT(m_impl == NULL); + if (!boost::log::aux::is_power_of_2(block_size)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Interprocess message queue block size is not a power of 2")); + try + { + m_impl = new implementation(open_mode::open_or_create, name, capacity, static_cast< size_type >(boost::alignment::align_up(block_size, BOOST_LOG_CPU_CACHE_LINE_SIZE)), oflow_policy, perms); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(name); + throw; + } + catch (boost::interprocess::interprocess_exception& e) + { + BOOST_THROW_EXCEPTION(boost::enable_error_info(system_error(boost::system::error_code(e.get_native_error(), boost::system::system_category()), e.what())) << boost::log::ipc::object_name_info(name)); + } +} + +BOOST_LOG_API void reliable_message_queue::open(object_name const& name, overflow_policy oflow_policy, permissions const&) +{ + BOOST_ASSERT(m_impl == NULL); + try + { + m_impl = new implementation(open_mode::open_only, name, oflow_policy); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(name); + throw; + } + catch (boost::interprocess::interprocess_exception& e) + { + BOOST_THROW_EXCEPTION(boost::enable_error_info(system_error(boost::system::error_code(e.get_native_error(), boost::system::system_category()), e.what())) << boost::log::ipc::object_name_info(name)); + } +} + +BOOST_LOG_API void reliable_message_queue::clear() +{ + BOOST_ASSERT(m_impl != NULL); + try + { + m_impl->clear(); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API object_name const& reliable_message_queue::name() const +{ + BOOST_ASSERT(m_impl != NULL); + return m_impl->name(); +} + +BOOST_LOG_API uint32_t reliable_message_queue::capacity() const +{ + BOOST_ASSERT(m_impl != NULL); + return m_impl->capacity(); +} + +BOOST_LOG_API reliable_message_queue::size_type reliable_message_queue::block_size() const +{ + BOOST_ASSERT(m_impl != NULL); + return m_impl->block_size(); +} + +BOOST_LOG_API void reliable_message_queue::stop_local() +{ + BOOST_ASSERT(m_impl != NULL); + try + { + m_impl->stop_local(); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::reset_local() +{ + BOOST_ASSERT(m_impl != NULL); + try + { + m_impl->reset_local(); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::do_close() BOOST_NOEXCEPT +{ + delete m_impl; + m_impl = NULL; +} + +BOOST_LOG_API reliable_message_queue::operation_result reliable_message_queue::send(void const* message_data, size_type message_size) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->send(message_data, message_size); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API bool reliable_message_queue::try_send(void const* message_data, size_type message_size) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->try_send(message_data, message_size); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API reliable_message_queue::operation_result reliable_message_queue::do_receive(receive_handler handler, void* state) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->receive(handler, state); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API bool reliable_message_queue::do_try_receive(receive_handler handler, void* state) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->try_receive(handler, state); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +//! Fixed buffer receive handler +BOOST_LOG_API void reliable_message_queue::fixed_buffer_receive_handler(void* state, const void* data, size_type size) +{ + fixed_buffer_state* p = static_cast< fixed_buffer_state* >(state); + if (BOOST_UNLIKELY(size > p->size)) + BOOST_THROW_EXCEPTION(bad_alloc("Buffer too small to receive the message")); + + std::memcpy(p->data, data, size); + p->data += size; + p->size -= size; +} + +BOOST_LOG_API void reliable_message_queue::remove(object_name const& name) +{ + boost::interprocess::shared_memory_object::remove(name.c_str()); +} + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/posix/ipc_sync_wrappers.hpp b/src/boost/libs/log/src/posix/ipc_sync_wrappers.hpp new file mode 100644 index 00000000..b32acba2 --- /dev/null +++ b/src/boost/libs/log/src/posix/ipc_sync_wrappers.hpp @@ -0,0 +1,267 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 posix/ipc_sync_wrappers.hpp + * \author Andrey Semashev + * \date 05.01.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_POSIX_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ +#define BOOST_LOG_POSIX_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <pthread.h> +#include <cerrno> +#include <cstddef> +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +// Use Boost.Interprocess to detect if process-shared pthread primitives are supported +#include <boost/interprocess/detail/workaround.hpp> +#if !defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) +#include <boost/core/explicit_operator_bool.hpp> +#include <boost/interprocess/sync/interprocess_mutex.hpp> +#include <boost/interprocess/sync/interprocess_condition.hpp> +#undef BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST +#endif +#include <boost/log/exceptions.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +namespace aux { + +#if defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + +#if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) +struct BOOST_SYMBOL_VISIBLE lock_owner_dead {}; +#endif + +//! Pthread mutex attributes +struct pthread_mutex_attributes +{ + pthread_mutexattr_t attrs; + + pthread_mutex_attributes() + { + int err = pthread_mutexattr_init(&this->attrs); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread mutex attributes", (err)); + } + + ~pthread_mutex_attributes() + { + BOOST_VERIFY(pthread_mutexattr_destroy(&this->attrs) == 0); + } + + BOOST_DELETED_FUNCTION(pthread_mutex_attributes(pthread_mutex_attributes const&)) + BOOST_DELETED_FUNCTION(pthread_mutex_attributes& operator=(pthread_mutex_attributes const&)) +}; + +//! Pthread condifion variable attributes +struct pthread_condition_variable_attributes +{ + pthread_condattr_t attrs; + + pthread_condition_variable_attributes() + { + int err = pthread_condattr_init(&this->attrs); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread condition variable attributes", (err)); + } + + ~pthread_condition_variable_attributes() + { + BOOST_VERIFY(pthread_condattr_destroy(&this->attrs) == 0); + } + + BOOST_DELETED_FUNCTION(pthread_condition_variable_attributes(pthread_condition_variable_attributes const&)) + BOOST_DELETED_FUNCTION(pthread_condition_variable_attributes& operator=(pthread_condition_variable_attributes const&)) +}; + +//! Interprocess mutex wrapper +struct interprocess_mutex +{ + struct auto_unlock + { + explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {} + ~auto_unlock() { m_mutex.unlock(); } + + BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&)) + BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&)) + + private: + interprocess_mutex& m_mutex; + }; + + pthread_mutex_t mutex; + + interprocess_mutex() + { + pthread_mutex_attributes attrs; + int err = pthread_mutexattr_settype(&attrs.attrs, PTHREAD_MUTEX_NORMAL); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to set pthread mutex type", (err)); + err = pthread_mutexattr_setpshared(&attrs.attrs, PTHREAD_PROCESS_SHARED); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to make pthread mutex process-shared", (err)); +#if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) + err = pthread_mutexattr_setrobust(&attrs.attrs, PTHREAD_MUTEX_ROBUST); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to make pthread mutex robust", (err)); +#endif + + err = pthread_mutex_init(&this->mutex, &attrs.attrs); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread mutex", (err)); + } + + ~interprocess_mutex() + { + BOOST_VERIFY(pthread_mutex_destroy(&this->mutex) == 0); + } + + void lock() + { + int err = pthread_mutex_lock(&this->mutex); +#if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) + if (BOOST_UNLIKELY(err == EOWNERDEAD)) + throw lock_owner_dead(); +#endif + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to lock pthread mutex", (err)); + } + + void unlock() BOOST_NOEXCEPT + { + BOOST_VERIFY(pthread_mutex_unlock(&this->mutex) == 0); + } + +#if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) + void recover() + { + int err = pthread_mutex_consistent(&this->mutex); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to recover pthread mutex from a crashed thread", (err)); + } +#endif + + BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&)) + BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&)) +}; + +//! Interprocess condition variable wrapper +struct interprocess_condition_variable +{ + pthread_cond_t cond; + + interprocess_condition_variable() + { + pthread_condition_variable_attributes attrs; + int err = pthread_condattr_setpshared(&attrs.attrs, PTHREAD_PROCESS_SHARED); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to make pthread condition variable process-shared", (err)); + + err = pthread_cond_init(&this->cond, &attrs.attrs); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread condition variable", (err)); + } + + ~interprocess_condition_variable() + { + BOOST_VERIFY(pthread_cond_destroy(&this->cond) == 0); + } + + void notify_one() + { + int err = pthread_cond_signal(&this->cond); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to notify one thread on a pthread condition variable", (err)); + } + + void notify_all() + { + int err = pthread_cond_broadcast(&this->cond); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to notify all threads on a pthread condition variable", (err)); + } + + void wait(interprocess_mutex& mutex) + { + int err = pthread_cond_wait(&this->cond, &mutex.mutex); + if (BOOST_UNLIKELY(err != 0)) + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to wait on a pthread condition variable", (err)); + } + + BOOST_DELETED_FUNCTION(interprocess_condition_variable(interprocess_condition_variable const&)) + BOOST_DELETED_FUNCTION(interprocess_condition_variable& operator=(interprocess_condition_variable const&)) +}; + +#else // defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + +// If there are no process-shared pthread primitives, use whatever emulation Boost.Interprocess implements +struct interprocess_mutex +{ + struct auto_unlock + { + explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {} + ~auto_unlock() { m_mutex.unlock(); } + + BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&)) + BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&)) + + private: + interprocess_mutex& m_mutex; + }; + + BOOST_DEFAULTED_FUNCTION(interprocess_mutex(), {}) + + // Members to emulate a lock interface + typedef boost::interprocess::interprocess_mutex mutex_type; + + BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT() + bool operator! () const BOOST_NOEXCEPT { return false; } + mutex_type* mutex() BOOST_NOEXCEPT { return &m_mutex; } + + void lock() + { + m_mutex.lock(); + } + + void unlock() BOOST_NOEXCEPT + { + m_mutex.unlock(); + } + + mutex_type m_mutex; + + BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&)) + BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&)) +}; + + +typedef boost::interprocess::interprocess_condition interprocess_condition_variable; + +#endif // defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + +} // namespace aux + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_POSIX_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/posix/object_name.cpp b/src/boost/libs/log/src/posix/object_name.cpp new file mode 100644 index 00000000..d0238341 --- /dev/null +++ b/src/boost/libs/log/src/posix/object_name.cpp @@ -0,0 +1,170 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 posix/object_name.cpp + * \author Andrey Semashev + * \date 06.03.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <unistd.h> +#include <sys/types.h> +#if defined(__ANDROID__) && (__ANDROID_API__+0) < 21 +#include <sys/syscall.h> +#endif +#if !defined(BOOST_LOG_NO_GETPWUID_R) +#include <pwd.h> +#endif +#include <cstddef> +#include <cstring> +#include <limits> +#include <string> +#include <vector> +#include <boost/move/utility_core.hpp> +#include <boost/type_traits/make_unsigned.hpp> +#include <boost/spirit/include/karma_uint.hpp> +#include <boost/spirit/include/karma_generate.hpp> +#include <boost/log/utility/ipc/object_name.hpp> +#include <boost/log/detail/header.hpp> + +namespace karma = boost::spirit::karma; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +#if defined(__ANDROID__) && (__ANDROID_API__+0) < 21 +// Until Android API version 21 NDK does not define getsid wrapper in libc, although there is the corresponding syscall +inline pid_t getsid(pid_t pid) BOOST_NOEXCEPT +{ + return static_cast< pid_t >(::syscall(__NR_getsid, pid)); +} +#endif + +//! Formats an integer identifier into the string +template< typename Identifier > +inline void format_id(Identifier id, std::string& str) +{ + // Note: in the code below, avoid involving locale for string formatting to make sure the names are as stable as possible + typedef typename boost::make_unsigned< Identifier >::type unsigned_id_t; + char buf[std::numeric_limits< unsigned_id_t >::digits10 + 2]; + char* p = buf; + + typedef karma::uint_generator< unsigned_id_t, 10 > unsigned_id_gen; + karma::generate(p, unsigned_id_gen(), static_cast< unsigned_id_t >(id)); + str.append(buf, p); +} + +//! Returns a prefix string for a shared resource according to the scope +std::string get_scope_prefix(object_name::scope ns) +{ + std::string prefix = "/boost.log."; + switch (ns) + { + case object_name::process_group: + { + prefix.append("pgid."); +#if !defined(BOOST_LOG_NO_GETPGRP) + format_id(getpgrp(), prefix); +#else + format_id(getuid(), prefix); +#endif + } + break; + + case object_name::session: + { + prefix.append("sid."); +#if !defined(BOOST_LOG_NO_GETSID) + format_id(getsid(0), prefix); +#else + format_id(getuid(), prefix); +#endif + } + break; + + case object_name::user: + { + const uid_t uid = getuid(); + +#if !defined(BOOST_LOG_NO_GETPWUID_R) + long limit = sysconf(_SC_GETPW_R_SIZE_MAX); + if (limit <= 0) + limit = 65536; + std::vector< char > string_storage; + string_storage.resize(static_cast< std::size_t >(limit)); + passwd pwd = {}, *result = NULL; + + try + { + const int err = getpwuid_r(uid, &pwd, &string_storage[0], string_storage.size(), &result); + if (err == 0 && result && result->pw_name) + { + prefix += "user."; + prefix += result->pw_name; + } + else + { + prefix += "uid."; + format_id(uid, prefix); + } + + // Avoid leaving sensitive data in memory, if there is any + std::memset(&pwd, 0, sizeof(pwd)); + std::memset(&string_storage[0], 0, string_storage.size()); + } + catch (...) + { + std::memset(&pwd, 0, sizeof(pwd)); + std::memset(&string_storage[0], 0, string_storage.size()); + throw; + } +#else + prefix += "uid."; + format_id(uid, prefix); +#endif + } + break; + + default: + prefix += "global"; + break; + } + + prefix.push_back('.'); + + return BOOST_LOG_NRVO_RESULT(prefix); +} + +} // namespace + +//! Constructor from the object name +BOOST_LOG_API object_name::object_name(scope ns, const char* str) : + m_name(get_scope_prefix(ns) + str) +{ +} + +//! Constructor from the object name +BOOST_LOG_API object_name::object_name(scope ns, std::string const& str) : + m_name(get_scope_prefix(ns) + str) +{ +} + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/process_id.cpp b/src/boost/libs/log/src/process_id.cpp new file mode 100644 index 00000000..e5dfd77b --- /dev/null +++ b/src/boost/libs/log/src/process_id.cpp @@ -0,0 +1,123 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 process_id.cpp + * \author Andrey Semashev + * \date 12.09.2009 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <iostream> +#include <boost/log/detail/process_id.hpp> +#include "id_formatting.hpp" + +#if defined(BOOST_WINDOWS) + +#include <windows.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +enum { pid_size = sizeof(GetCurrentProcessId()) }; + +namespace this_process { + + //! The function returns current process identifier + BOOST_LOG_API process::id get_id() + { + return process::id(GetCurrentProcessId()); + } + +} // namespace this_process + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#else // defined(BOOST_WINDOWS) + +#include <unistd.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +namespace this_process { + + //! The function returns current process identifier + BOOST_LOG_API process::id get_id() + { + // According to POSIX, pid_t should always be an integer type: + // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html + return process::id(getpid()); + } + +} // namespace this_process + +enum { pid_size = sizeof(pid_t) }; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +template< typename CharT, typename TraitsT > +BOOST_LOG_API std::basic_ostream< CharT, TraitsT >& +operator<< (std::basic_ostream< CharT, TraitsT >& strm, process::id const& pid) +{ + if (strm.good()) + { + CharT buf[pid_size * 2 + 3]; // 2 chars per byte + 3 chars for the leading 0x and terminating zero + format_id< pid_size >(buf, sizeof(buf) / sizeof(*buf), pid.native_id(), (strm.flags() & std::ios_base::uppercase) != 0); + + strm << buf; + } + + return strm; +} + +#if defined(BOOST_LOG_USE_CHAR) +template BOOST_LOG_API +std::basic_ostream< char, std::char_traits< char > >& +operator<< (std::basic_ostream< char, std::char_traits< char > >& strm, process::id const& pid); +#endif // defined(BOOST_LOG_USE_CHAR) + +#if defined(BOOST_LOG_USE_WCHAR_T) +template BOOST_LOG_API +std::basic_ostream< wchar_t, std::char_traits< wchar_t > >& +operator<< (std::basic_ostream< wchar_t, std::char_traits< wchar_t > >& strm, process::id const& pid); +#endif // defined(BOOST_LOG_USE_WCHAR_T) + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/process_name.cpp b/src/boost/libs/log/src/process_name.cpp new file mode 100644 index 00000000..864defac --- /dev/null +++ b/src/boost/libs/log/src/process_name.cpp @@ -0,0 +1,187 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 process_name.cpp + * \author Andrey Semashev + * \date 29.07.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * The code in this file is based on information on this page: + * + * http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe + */ + +#include <boost/log/detail/config.hpp> +#include <climits> // PATH_MAX +#include <boost/log/attributes/current_process_name.hpp> +#include <boost/filesystem/path.hpp> + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#if defined(BOOST_WINDOWS) + +#include <windows.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! The function returns the current process name +BOOST_LOG_API std::string get_process_name() +{ + std::wstring buf; + buf.resize(PATH_MAX); + do + { + unsigned int len = GetModuleFileNameW(NULL, &buf[0], static_cast< unsigned int >(buf.size())); + if (len < buf.size()) + { + buf.resize(len); + break; + } + + buf.resize(buf.size() * 2); + } + while (buf.size() < 65536); + + return filesystem::path(buf).filename().string(); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + +#include <cstring> +#include <mach-o/dyld.h> +#include <boost/cstdint.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! The function returns the current process name +BOOST_LOG_API std::string get_process_name() +{ + std::string buf; + buf.resize(PATH_MAX); + while (true) + { + uint32_t size = static_cast< uint32_t >(buf.size()); + if (_NSGetExecutablePath(&buf[0], &size) == 0) + { + buf.resize(std::strlen(&buf[0])); + break; + } + + buf.resize(size); + } + + return filesystem::path(buf).filename().string(); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#elif defined(__FreeBSD__) + +#include <stddef.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <boost/lexical_cast.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! The function returns the current process name +BOOST_LOG_API std::string get_process_name() +{ +#if defined(KERN_PROC_PATHNAME) + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + char buf[PATH_MAX] = {}; + size_t cb = sizeof(buf); + if (sysctl(mib, 4, buf, &cb, NULL, 0) == 0) + return filesystem::path(buf).filename().string(); +#endif + + if (filesystem::exists("/proc/curproc/file")) + return filesystem::read_symlink("/proc/curproc/file").filename().string(); + + return boost::lexical_cast< std::string >(getpid()); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#else + +#include <unistd.h> +#include <boost/lexical_cast.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! The function returns the current process name +BOOST_LOG_API std::string get_process_name() +{ + if (filesystem::exists("/proc/self/exe")) + return filesystem::read_symlink("/proc/self/exe").filename().string(); + + if (filesystem::exists("/proc/curproc/file")) + return filesystem::read_symlink("/proc/curproc/file").filename().string(); + + if (filesystem::exists("/proc/curproc/exe")) + return filesystem::read_symlink("/proc/curproc/exe").filename().string(); + + return boost::lexical_cast< std::string >(getpid()); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif diff --git a/src/boost/libs/log/src/record_ostream.cpp b/src/boost/libs/log/src/record_ostream.cpp new file mode 100644 index 00000000..27ed3d09 --- /dev/null +++ b/src/boost/libs/log/src/record_ostream.cpp @@ -0,0 +1,183 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 record_ostream.cpp + * \author Andrey Semashev + * \date 17.04.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <locale> +#include <utility> +#include <boost/log/sources/record_ostream.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/attributes/attribute_value_impl.hpp> +#include <boost/log/expressions/message.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/tss.hpp> +#endif +#include "unique_ptr.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +//! The function initializes the stream and the stream buffer +template< typename CharT > +BOOST_LOG_API void basic_record_ostream< CharT >::init_stream() +{ + base_type::init_stream(); + base_type::imbue(std::locale()); + if (m_record) + { + typedef attributes::attribute_value_impl< string_type > message_impl_type; + intrusive_ptr< message_impl_type > p = new message_impl_type(string_type()); + attribute_value value(p); + + // This may fail if the record already has Message attribute + std::pair< attribute_value_set::const_iterator, bool > res = + m_record->attribute_values().insert(expressions::tag::message::get_name(), value); + if (!res.second) + const_cast< attribute_value& >(res.first->second).swap(value); + + base_type::attach(const_cast< string_type& >(p->get())); + } +} +//! The function resets the stream into a detached (default initialized) state +template< typename CharT > +BOOST_LOG_API void basic_record_ostream< CharT >::detach_from_record() BOOST_NOEXCEPT +{ + if (m_record) + { + base_type::detach(); + m_record = NULL; + base_type::exceptions(base_type::goodbit); + } +} + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! The pool of stream compounds +template< typename CharT > +class stream_compound_pool : + public log::aux::lazy_singleton< + stream_compound_pool< CharT >, +#if !defined(BOOST_LOG_NO_THREADS) + thread_specific_ptr< stream_compound_pool< CharT > > +#else + log::aux::unique_ptr< stream_compound_pool< CharT > > +#endif + > +{ + //! Self type + typedef stream_compound_pool< CharT > this_type; +#if !defined(BOOST_LOG_NO_THREADS) + //! Thread-specific pointer type + typedef thread_specific_ptr< this_type > tls_ptr_type; +#else + //! Thread-specific pointer type + typedef log::aux::unique_ptr< this_type > tls_ptr_type; +#endif + //! Singleton base type + typedef log::aux::lazy_singleton< + this_type, + tls_ptr_type + > base_type; + //! Stream compound type + typedef typename stream_provider< CharT >::stream_compound stream_compound_t; + +public: + //! Pooled stream compounds + stream_compound_t* m_Top; + + ~stream_compound_pool() + { + stream_compound_t* p = NULL; + while ((p = m_Top) != NULL) + { + m_Top = p->next; + delete p; + } + } + + //! The method returns pool instance + static stream_compound_pool& get() + { + tls_ptr_type& ptr = base_type::get(); + this_type* p = ptr.get(); + if (!p) + { + log::aux::unique_ptr< this_type > pNew(new this_type()); + ptr.reset(pNew.get()); + p = pNew.release(); + } + return *p; + } + +private: + stream_compound_pool() : m_Top(NULL) {} +}; + +} // namespace + +//! The method returns an allocated stream compound +template< typename CharT > +BOOST_LOG_API typename stream_provider< CharT >::stream_compound* +stream_provider< CharT >::allocate_compound(record& rec) +{ + stream_compound_pool< char_type >& pool = stream_compound_pool< char_type >::get(); + if (pool.m_Top) + { + stream_compound* p = pool.m_Top; + pool.m_Top = p->next; + p->next = NULL; + p->stream.attach_record(rec); + return p; + } + else + return new stream_compound(rec); +} + +//! The method releases a compound +template< typename CharT > +BOOST_LOG_API void stream_provider< CharT >::release_compound(stream_compound* compound) BOOST_NOEXCEPT +{ + stream_compound_pool< char_type >& pool = stream_compound_pool< char_type >::get(); + compound->next = pool.m_Top; + pool.m_Top = compound; + compound->stream.detach_from_record(); +} + +//! Explicitly instantiate stream_provider implementation +#ifdef BOOST_LOG_USE_CHAR +template struct stream_provider< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template struct stream_provider< wchar_t >; +#endif + +} // namespace aux + +//! Explicitly instantiate basic_record_ostream implementation +#ifdef BOOST_LOG_USE_CHAR +template class basic_record_ostream< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template class basic_record_ostream< wchar_t >; +#endif + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/setup/default_filter_factory.cpp b/src/boost/libs/log/src/setup/default_filter_factory.cpp new file mode 100644 index 00000000..ff9106ef --- /dev/null +++ b/src/boost/libs/log/src/setup/default_filter_factory.cpp @@ -0,0 +1,356 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_filter_factory.hpp + * \author Andrey Semashev + * \date 29.05.2010 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + +#include <boost/log/detail/setup_config.hpp> +#include <string> +#include <boost/mpl/vector.hpp> +#include <boost/preprocessor/seq/enum.hpp> +#include <boost/spirit/include/qi_core.hpp> +#include <boost/log/trivial.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/expressions/predicates/has_attr.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/utility/functional/logical.hpp> +#include <boost/log/utility/functional/begins_with.hpp> +#include <boost/log/utility/functional/ends_with.hpp> +#include <boost/log/utility/functional/contains.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/detail/default_attribute_names.hpp> +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) +#include <boost/fusion/container/set.hpp> +#include <boost/fusion/sequence/intrinsic/at_key.hpp> +#include <boost/fusion/algorithm/iteration/for_each.hpp> +#endif +#include "default_filter_factory.hpp" +#include "parser_utils.hpp" +#include <boost/log/detail/header.hpp> + +namespace qi = boost::spirit::qi; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +//! A special filtering predicate that adopts the string operand to the attribute value character type +template< typename RelationT > +class string_predicate : + public RelationT +{ +private: + template< typename StringT > + struct initializer + { + typedef void result_type; + + explicit initializer(StringT const& val) : m_initializer(val) + { + } + + template< typename T > + result_type operator() (T& val) const + { + try + { + log::aux::code_convert(m_initializer, val); + } + catch (...) + { + val.clear(); + } + } + + private: + StringT const& m_initializer; + }; + +public: + typedef RelationT relation_type; + typedef typename relation_type::result_type result_type; + + template< typename StringT > + string_predicate(relation_type const& rel, StringT const& operand) : relation_type(rel) + { + fusion::for_each(m_operands, initializer< StringT >(operand)); + } + + template< typename T > + result_type operator() (T const& val) const + { + typedef std::basic_string< typename T::value_type > operand_type; + return relation_type::operator() (val, fusion::at_key< operand_type >(m_operands)); + } + +private: + fusion::set< std::string, std::wstring > m_operands; +}; + +#else // defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +//! A special filtering predicate that adopts the string operand to the attribute value character type +template< typename RelationT > +class string_predicate : + public RelationT +{ +private: +#if defined(BOOST_LOG_USE_CHAR) + typedef std::basic_string< char > string_type; +#elif defined(BOOST_LOG_USE_WCHAR_T) + typedef std::basic_string< wchar_t > string_type; +#else +#error Boost.Log: Inconsistent character configuration +#endif + +public: + typedef RelationT relation_type; + typedef typename relation_type::result_type result_type; + +public: + string_predicate(relation_type const& rel, string_type const& operand) : relation_type(rel), m_operand(operand) + { + } + + template< typename T > + result_type operator() (T const& val) const + { + return relation_type::operator() (val, m_operand); + } + +private: + const string_type m_operand; +}; + +#endif // defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +//! A special filtering predicate that adopts the severity level or string operand to the attribute value character type +template< typename RelationT > +class severity_or_string_predicate : + public string_predicate< RelationT > +{ +private: + typedef string_predicate< RelationT > base_type; + +public: + typedef typename base_type::relation_type relation_type; + typedef typename base_type::result_type result_type; + +public: + template< typename StringT > + severity_or_string_predicate(relation_type const& rel, StringT const& string_operand, boost::log::trivial::severity_level severity_operand) : + base_type(rel, string_operand), + m_severity_operand(severity_operand) + { + } + + using base_type::operator(); + + result_type operator() (boost::log::trivial::severity_level val) const + { + return relation_type::operator() (val, m_severity_operand); + } + +private: + const boost::log::trivial::severity_level m_severity_operand; +}; + +//! A filtering predicate for numeric relations +template< typename NumericT, typename RelationT > +class numeric_predicate : + public string_predicate< RelationT > +{ + typedef string_predicate< RelationT > base_type; + +public: + typedef NumericT numeric_type; + typedef typename base_type::relation_type relation_type; + typedef typename base_type::result_type result_type; + +public: + template< typename StringT > + numeric_predicate(relation_type const& rel, StringT const& string_operand, numeric_type numeric_operand) : + base_type(rel, string_operand), + m_numeric_operand(numeric_operand) + { + } + + template< typename CharT, typename TraitsT, typename AllocatorT > + result_type operator() (std::basic_string< CharT, TraitsT, AllocatorT > const& val) const + { + return base_type::operator() (val); + } + + template< typename CharT, typename TraitsT > + result_type operator() (basic_string_literal< CharT, TraitsT > const& val) const + { + return base_type::operator() (val); + } + + template< typename T > + result_type operator() (T const& val) const + { + return relation_type::operator() (val, m_numeric_operand); + } + +private: + const numeric_type m_numeric_operand; +}; + +typedef mpl::vector< + BOOST_PP_SEQ_ENUM(BOOST_LOG_STANDARD_FLOATING_POINT_TYPES()BOOST_LOG_STANDARD_STRING_TYPES()) +> floating_point_and_string_types; + +} // namespace + +//! The callback for equality relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_equality_relation(attribute_name const& name, string_type const& arg) +{ + return parse_argument< equal_to >(name, arg); +} + +//! The callback for inequality relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_inequality_relation(attribute_name const& name, string_type const& arg) +{ + return parse_argument< not_equal_to >(name, arg); +} + +//! The callback for less relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_less_relation(attribute_name const& name, string_type const& arg) +{ + return parse_argument< less >(name, arg); +} + +//! The callback for greater relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_greater_relation(attribute_name const& name, string_type const& arg) +{ + return parse_argument< greater >(name, arg); +} + +//! The callback for less or equal relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_less_or_equal_relation(attribute_name const& name, string_type const& arg) +{ + return parse_argument< less_equal >(name, arg); +} + +//! The callback for greater or equal relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_greater_or_equal_relation(attribute_name const& name, string_type const& arg) +{ + return parse_argument< greater_equal >(name, arg); +} + +//! The callback for custom relation filter +template< typename CharT > +filter default_filter_factory< CharT >::on_custom_relation(attribute_name const& name, string_type const& rel, string_type const& arg) +{ + typedef log::aux::char_constants< char_type > constants; + + if (rel == constants::begins_with_keyword()) + { + typedef string_predicate< begins_with_fun > predicate; + return predicate_wrapper< log::string_types, predicate >(name, predicate(begins_with_fun(), arg)); + } + else if (rel == constants::ends_with_keyword()) + { + typedef string_predicate< ends_with_fun > predicate; + return predicate_wrapper< log::string_types, predicate >(name, predicate(ends_with_fun(), arg)); + } + else if (rel == constants::contains_keyword()) + { + typedef string_predicate< contains_fun > predicate; + return predicate_wrapper< log::string_types, predicate >(name, predicate(contains_fun(), arg)); + } + else if (rel != constants::matches_keyword()) + { + BOOST_LOG_THROW_DESCR(parse_error, "The custom attribute relation \"" + log::aux::to_narrow(rel) + "\" is not supported"); + } + + return parse_matches_relation(name, arg); +} + + +//! The function parses the argument value for a binary relation and constructs the corresponding filter +template< typename CharT > +template< typename RelationT > +filter default_filter_factory< CharT >::parse_argument(attribute_name const& name, string_type const& arg) +{ + const char_type* begin = arg.c_str(); + const char_type* const end = begin + arg.size(); + + double real_val = 0.0; + const qi::real_parser< double, qi::strict_real_policies< double > > real_; + if (qi::parse(begin, end, real_, real_val) && begin == end) + { + typedef numeric_predicate< double, RelationT > predicate; + typedef floating_point_and_string_types value_types; + return predicate_wrapper< value_types, predicate >(name, predicate(RelationT(), arg, real_val)); + } + else + { + begin = arg.c_str(); + long int_val = 0; + if (qi::parse(begin, end, qi::long_, int_val) && begin == end) + { + typedef numeric_predicate< long, RelationT > predicate; + typedef default_attribute_value_types value_types; + return predicate_wrapper< value_types, predicate >(name, predicate(RelationT(), arg, int_val)); + } + else + { + boost::log::trivial::severity_level lvl; + if (name == boost::log::aux::default_attribute_names::severity() && boost::log::trivial::from_string(arg.data(), arg.size(), lvl)) + { + typedef severity_or_string_predicate< RelationT > predicate; + typedef mpl::vector< boost::log::trivial::severity_level, BOOST_PP_SEQ_ENUM(BOOST_LOG_STANDARD_STRING_TYPES()) > value_types; + return predicate_wrapper< value_types, predicate >(name, predicate(RelationT(), arg, lvl)); + } + else + { + typedef string_predicate< RelationT > predicate; + return predicate_wrapper< log::string_types, predicate >(name, predicate(RelationT(), arg)); + } + } + } +} + +// Explicitly instantiate factory implementation +#ifdef BOOST_LOG_USE_CHAR +template class default_filter_factory< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template class default_filter_factory< wchar_t >; +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) diff --git a/src/boost/libs/log/src/setup/default_filter_factory.hpp b/src/boost/libs/log/src/setup/default_filter_factory.hpp new file mode 100644 index 00000000..455eb424 --- /dev/null +++ b/src/boost/libs/log/src/setup/default_filter_factory.hpp @@ -0,0 +1,106 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_filter_factory.hpp + * \author Andrey Semashev + * \date 29.05.2010 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_DEFAULT_FILTER_FACTORY_HPP_INCLUDED_ +#define BOOST_DEFAULT_FILTER_FACTORY_HPP_INCLUDED_ + +#include <boost/log/detail/setup_config.hpp> +#include <cstddef> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/utility/setup/filter_parser.hpp> +#include <boost/log/utility/functional/save_result.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Relation predicate wrapper +template< typename ValueT, typename PredicateT > +struct predicate_wrapper +{ + typedef typename PredicateT::result_type result_type; + + explicit predicate_wrapper(attribute_name const& name, PredicateT const& pred) : m_name(name), m_visitor(pred) + { + } + + template< typename T > + result_type operator() (T const& arg) const + { + bool res = false; + boost::log::visit< ValueT >(m_name, arg, save_result_wrapper< PredicateT const&, bool >(m_visitor, res)); + return res; + } + +private: + attribute_name m_name; + const PredicateT m_visitor; +}; + +//! The default filter factory that supports creating filters for the standard types (see utility/type_dispatch/standard_types.hpp) +template< typename CharT > +class default_filter_factory : + public filter_factory< CharT > +{ +private: + //! Base type + typedef filter_factory< CharT > base_type; + //! Self type + typedef default_filter_factory< CharT > this_type; + +public: + // Type imports + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + + //! The callback for equality relation filter + virtual filter on_equality_relation(attribute_name const& name, string_type const& arg); + //! The callback for inequality relation filter + virtual filter on_inequality_relation(attribute_name const& name, string_type const& arg); + //! The callback for less relation filter + virtual filter on_less_relation(attribute_name const& name, string_type const& arg); + //! The callback for greater relation filter + virtual filter on_greater_relation(attribute_name const& name, string_type const& arg); + //! The callback for less or equal relation filter + virtual filter on_less_or_equal_relation(attribute_name const& name, string_type const& arg); + //! The callback for greater or equal relation filter + virtual filter on_greater_or_equal_relation(attribute_name const& name, string_type const& arg); + + //! The callback for custom relation filter + virtual filter on_custom_relation(attribute_name const& name, string_type const& rel, string_type const& arg); + +private: + //! The function parses the argument value for a binary relation and constructs the corresponding filter + template< typename RelationT > + static filter parse_argument(attribute_name const& name, string_type const& arg); +}; + +//! The function parses the "matches" relation +template< typename CharT > +filter parse_matches_relation(attribute_name const& name, std::basic_string< CharT > const& operand); + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_DEFAULT_FILTER_FACTORY_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/setup/default_formatter_factory.cpp b/src/boost/libs/log/src/setup/default_formatter_factory.cpp new file mode 100644 index 00000000..15a0b838 --- /dev/null +++ b/src/boost/libs/log/src/setup/default_formatter_factory.cpp @@ -0,0 +1,289 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_formatter_factory.cpp + * \author Andrey Semashev + * \date 14.07.2013 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + +#include <boost/log/detail/setup_config.hpp> +#include <cstddef> +#include <ctime> +#include <boost/mpl/vector.hpp> +#include <boost/mpl/vector/vector40.hpp> +#include <boost/preprocessor/cat.hpp> +#include <boost/preprocessor/seq/enum.hpp> +#include <boost/preprocessor/seq/size.hpp> +#include <boost/date_time/special_defs.hpp> +#include <boost/date_time/gregorian/gregorian_types.hpp> +#include <boost/date_time/local_time/local_time_types.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/log/trivial.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/type_dispatch/date_time_types.hpp> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/detail/snprintf.hpp> +#include <boost/log/detail/process_id.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/log/detail/thread_id.hpp> +#endif +#include <boost/log/attributes/named_scope.hpp> +#include "default_formatter_factory.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#if !defined(BOOST_LOG_NO_THREADS) +#define BOOST_LOG_AUX_THREAD_ID_TYPE() (boost::log::aux::thread::id) +#else +#define BOOST_LOG_AUX_THREAD_ID_TYPE() +#endif + +#define BOOST_LOG_AUX_LOG_ATTRIBUTE_VALUE_TYPES()\ + (boost::log::trivial::severity_level)\ + (boost::log::attributes::named_scope_list)\ + (boost::log::aux::process::id)\ + BOOST_LOG_AUX_THREAD_ID_TYPE() + +// The list of the attribute value types supported by the default formatter. Note that we have to exclude std::time_t +// as it is an integral type, as well as double from the native time duration types - these are part of arithmetic types already. +#define BOOST_LOG_AUX_LOG_DEFAULT_VALUE_TYPES()\ + BOOST_LOG_DEFAULT_ATTRIBUTE_VALUE_TYPES()\ + (std::tm)\ + BOOST_LOG_BOOST_DATE_TYPES()\ + BOOST_LOG_BOOST_TIME_DURATION_TYPES()\ + BOOST_LOG_BOOST_TIME_PERIOD_TYPES()\ + BOOST_LOG_AUX_LOG_ATTRIBUTE_VALUE_TYPES() + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! The default formatter generated by the default formatter factory +template< typename CharT > +class default_formatter +{ +public: + typedef void result_type; + +private: + //! Attribute value visitor + struct visitor + { + typedef void result_type; + + explicit visitor(basic_formatting_ostream< CharT >& strm) : m_strm(strm) + { + } + + template< typename T > + void operator() (T const& value) const + { + m_strm << value; + } + + void operator() (std::tm const& value) const + { + char buf[32]; + std::size_t len = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &value); + m_strm.write(buf, len); + } + + void operator() (boost::posix_time::ptime const& value) const + { + if (!value.is_special()) + { + std::tm t = boost::posix_time::to_tm(value); + char buf[32]; + std::size_t len = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &t); + std::size_t size = sizeof(buf) - len; + int res = boost::log::aux::snprintf(buf + len, size, ".%.6u", static_cast< unsigned int >(value.time_of_day().total_microseconds() % 1000000)); + if (res < 0) + buf[len] = '\0'; + else if (static_cast< std::size_t >(res) >= size) + len += size - 1; + else + len += res; + + m_strm.write(buf, len); + } + else + { + format_special_date_time(value); + } + } + + void operator() (boost::local_time::local_date_time const& value) const + { + if (!value.is_special()) + { + this->operator()(value.local_time()); + m_strm << ' ' << value.zone_as_posix_string(); + } + else + { + format_special_date_time(value); + } + } + + void operator() (boost::gregorian::date const& value) const + { + if (!value.is_special()) + { + std::tm t = boost::gregorian::to_tm(value); + char buf[32]; + std::size_t len = std::strftime(buf, sizeof(buf), "%Y-%m-%d", &t); + m_strm.write(buf, len); + } + else + { + format_special_date_time(value.as_special()); + } + } + + void operator() (boost::posix_time::time_duration const& value) const + { + if (!value.is_special()) + { + boost::posix_time::time_duration val = value; + if (val.is_negative()) + { + m_strm << '-'; + val = -val; + } + unsigned long long total_useconds = value.total_microseconds(); + unsigned long long hours = total_useconds / (3600ull * 1000000ull); + unsigned int minutes = static_cast< unsigned int >(total_useconds / (60ull * 1000000ull) % 60ull); + unsigned int seconds = static_cast< unsigned int >(total_useconds / 1000000ull % 60ull); + unsigned int useconds = static_cast< unsigned int >(total_useconds % 1000000ull); + char buf[64]; + int len = boost::log::aux::snprintf(buf, sizeof(buf), "%.2llu:%.2u:%.2u.%.6u", hours, minutes, seconds, useconds); + if (len > 0) + { + unsigned int size = static_cast< unsigned int >(len) >= sizeof(buf) ? static_cast< unsigned int >(sizeof(buf)) : static_cast< unsigned int >(len); + m_strm.write(buf, size); + } + } + else + { + format_special_date_time(value); + } + } + + void operator() (boost::gregorian::date_duration const& value) const + { + if (!value.is_special()) + { + m_strm << value.get_rep().as_number(); + } + else + { + format_special_date_time(value.get_rep().as_special()); + } + } + + template< typename PointRepT, typename DurationRepT > + void operator() (boost::date_time::period< PointRepT, DurationRepT > const& value) const + { + m_strm << '['; + this->operator()(value.begin()); + m_strm << '/'; + this->operator()(value.last()); + m_strm << ']'; + } + + private: + template< typename T > + void format_special_date_time(T const& value) const + { + if (value.is_not_a_date_time()) + m_strm << "not-a-date-time"; + else if (value.is_pos_infinity()) + m_strm << "+infinity"; + else if (value.is_neg_infinity()) + m_strm << "-infinity"; + } + + void format_special_date_time(boost::date_time::special_values value) const + { + switch (value) + { + case boost::date_time::not_a_date_time: + m_strm << "not-a-date-time"; + break; + case boost::date_time::pos_infin: + m_strm << "+infinity"; + break; + case boost::date_time::neg_infin: + m_strm << "-infinity"; + break; + default: + break; + } + } + + private: + basic_formatting_ostream< CharT >& m_strm; + }; + +public: + explicit default_formatter(attribute_name name) : m_attribute_name(name) + { + } + + result_type operator() (record_view const& rec, basic_formatting_ostream< CharT >& strm) const + { + typedef BOOST_PP_CAT(mpl::vector, BOOST_PP_SEQ_SIZE(BOOST_LOG_AUX_LOG_DEFAULT_VALUE_TYPES()))< + BOOST_PP_SEQ_ENUM(BOOST_LOG_AUX_LOG_DEFAULT_VALUE_TYPES()) + > value_types; + + boost::log::visit< value_types >(m_attribute_name, rec, visitor(strm)); + } + +private: + const attribute_name m_attribute_name; +}; + +} // namespace + +//! The callback for equality relation filter +template< typename CharT > +typename default_formatter_factory< CharT >::formatter_type +default_formatter_factory< CharT >::create_formatter(attribute_name const& name, args_map const& args) +{ + // No user-defined factory, shall use the most generic formatter we can ever imagine at this point + return formatter_type(default_formatter< CharT >(name)); +} + +// Explicitly instantiate factory implementation +#ifdef BOOST_LOG_USE_CHAR +template class default_formatter_factory< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template class default_formatter_factory< wchar_t >; +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) diff --git a/src/boost/libs/log/src/setup/default_formatter_factory.hpp b/src/boost/libs/log/src/setup/default_formatter_factory.hpp new file mode 100644 index 00000000..6359b3be --- /dev/null +++ b/src/boost/libs/log/src/setup/default_formatter_factory.hpp @@ -0,0 +1,59 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 default_formatter_factory.hpp + * \author Andrey Semashev + * \date 14.07.2013 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_DEFAULT_FORMATTER_FACTORY_HPP_INCLUDED_ +#define BOOST_DEFAULT_FORMATTER_FACTORY_HPP_INCLUDED_ + +#include <boost/log/detail/setup_config.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/utility/setup/formatter_parser.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! The default filter factory that supports creating filters for the standard types (see utility/type_dispatch/standard_types.hpp) +template< typename CharT > +class default_formatter_factory : + public formatter_factory< CharT > +{ + //! Base type + typedef formatter_factory< CharT > base_type; + //! Self type + typedef default_formatter_factory< CharT > this_type; + +public: + // Type imports + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::formatter_type formatter_type; + typedef typename base_type::args_map args_map; + + //! The function creates a formatter for the specified attribute. + virtual formatter_type create_formatter(attribute_name const& name, args_map const& args); +}; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_DEFAULT_FORMATTER_FACTORY_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/setup/filter_parser.cpp b/src/boost/libs/log/src/setup/filter_parser.cpp new file mode 100644 index 00000000..cf41bca0 --- /dev/null +++ b/src/boost/libs/log/src/setup/filter_parser.cpp @@ -0,0 +1,531 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filter_parser.cpp + * \author Andrey Semashev + * \date 31.03.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS + +#include <boost/log/detail/setup_config.hpp> +#include <cstddef> +#include <map> +#include <stack> +#include <string> +#include <sstream> +#include <stdexcept> +#include <utility> +#include <boost/assert.hpp> +#include <boost/none.hpp> +#include <boost/move/core.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/optional/optional.hpp> +#include <boost/utility/in_place_factory.hpp> +#include <boost/phoenix/core.hpp> +#include <boost/phoenix/bind/bind_function_object.hpp> +#include <boost/phoenix/operator/logical.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/setup/filter_parser.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/log/detail/locks.hpp> +#include <boost/log/detail/light_rw_mutex.hpp> +#endif // !defined(BOOST_LOG_NO_THREADS) +#include "parser_utils.hpp" +#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) +#include "default_filter_factory.hpp" +#endif +#include "spirit_encoding.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! Filter factories repository +template< typename CharT > +struct filters_repository : + public log::aux::lazy_singleton< filters_repository< CharT > > +{ + typedef CharT char_type; + typedef log::aux::lazy_singleton< filters_repository< char_type > > base_type; + typedef std::basic_string< char_type > string_type; + typedef filter_factory< char_type > filter_factory_type; + + //! Attribute name ordering predicate + struct attribute_name_order + { + typedef bool result_type; + result_type operator() (attribute_name const& left, attribute_name const& right) const + { + return left.id() < right.id(); + } + }; + + typedef std::map< attribute_name, shared_ptr< filter_factory_type >, attribute_name_order > factories_map; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) + friend class log::aux::lazy_singleton< filters_repository< char_type > >; +#else + friend class base_type; +#endif + +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + mutable log::aux::light_rw_mutex m_Mutex; +#endif + //! The map of filter factories + factories_map m_Map; +#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + //! Default factory + mutable aux::default_filter_factory< char_type > m_DefaultFactory; +#endif + + //! The method returns the filter factory for the specified attribute name + filter_factory_type& get_factory(attribute_name const& name) const + { + typename factories_map::const_iterator it = m_Map.find(name); + if (it != m_Map.end()) + { + return *it->second; + } + else + { +#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + return m_DefaultFactory; +#else + BOOST_LOG_THROW_DESCR(setup_error, "No filter factory registered for attribute " + name.string()); +#endif + } + } + +private: + filters_repository() + { + } +}; + +//! Filter parser +template< typename CharT > +class filter_parser +{ +private: + typedef CharT char_type; + typedef const char_type* iterator_type; + typedef typename log::aux::encoding< char_type >::type encoding; + typedef log::aux::encoding_specific< encoding > encoding_specific; + typedef std::basic_string< char_type > string_type; + typedef log::aux::char_constants< char_type > constants; + typedef filter_factory< char_type > filter_factory_type; + + typedef filter (filter_factory_type::*comparison_relation_handler_t)(attribute_name const&, string_type const&); + +private: + //! Parsed attribute name + mutable attribute_name m_AttributeName; + //! The second operand of a relation + mutable optional< string_type > m_Operand; + //! Comparison relation handler + comparison_relation_handler_t m_ComparisonRelation; + //! The custom relation string + mutable string_type m_CustomRelation; + + //! Filter subexpressions as they are parsed + mutable std::stack< filter > m_Subexpressions; + +public: + //! Constructor + filter_parser() : + m_ComparisonRelation(NULL) + { + } + + //! The method returns the constructed filter + filter get_filter() + { + if (m_Subexpressions.empty()) + return filter(); + return boost::move(m_Subexpressions.top()); + } + + //! The pethod parses filter from the string + void parse(iterator_type& begin, iterator_type end, unsigned int depth = 0) + { + typedef void (filter_parser::*logical_op_t)(); + logical_op_t logical_op = NULL; + iterator_type p = constants::trim_spaces_left(begin, end); + while (p != end) + { + // Parse subexpression + parse_subexpression(p, end, depth); + if (logical_op) + { + // This was the right-hand subexpression. Compose the two top subexpressions into a single filter. + (this->*logical_op)(); + logical_op = NULL; + } + + p = constants::trim_spaces_left(p, end); + if (p != end) + { + char_type c = *p; + iterator_type next = p + 1; + if (c == constants::char_paren_bracket_right) + { + // The subexpression has ended + if (depth == 0) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unmatched closing parenthesis"); + + p = next; + --depth; + break; + } + else if (c == constants::char_and || scan_keyword(p, end, next, constants::and_keyword())) + { + logical_op = &filter_parser::on_and; + } + else if (c == constants::char_or || scan_keyword(p, end, next, constants::or_keyword())) + { + logical_op = &filter_parser::on_or; + } + else + { + BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character encountered"); + } + + p = constants::trim_spaces_left(next, end); + } + else + break; + } + + if (logical_op) + { + BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: logical operation without the right-hand subexpression"); + } + + if (p == end && depth > 0) + { + BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unterminated parenthesis"); + } + + begin = p; + } + +private: + //! The method parses a single subexpression + void parse_subexpression(iterator_type& begin, iterator_type end, unsigned int depth) + { + bool negated = false, negation_present = false, done = false; + iterator_type p = begin; + + while (p != end) + { + char_type c = *p; + iterator_type next = p + 1; + if (c == constants::char_percent) + { + // We found an attribute placeholder + iterator_type start = constants::trim_spaces_left(next, end); + p = constants::scan_attr_placeholder(start, end); + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string"); + + on_attribute_name(start, p); + + p = constants::trim_spaces_left(p, end); + if (p == end || *p != constants::char_percent) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the filter string"); + + // Skip the closing char_percent + p = constants::trim_spaces_left(++p, end); + + // If the filter has negation operator, do not expect a relation (i.e. "!%attr% > 1" is not valid because "!%attr%" is interpreted as an attribute presence test) + if (!negation_present) + p = parse_relation(p, end); + else + on_relation_complete(); + } + else if (c == constants::char_exclamation || scan_keyword(p, end, next, constants::not_keyword())) + { + // We found negation operation. Parse the subexpression to be negated. + negated ^= true; + negation_present = true; + p = constants::trim_spaces_left(next, end); + continue; + } + else if (c == constants::char_paren_bracket_left) + { + // We found a nested subexpression + parse(next, end, depth + 1); + p = next; + } + else + { + BOOST_LOG_THROW_DESCR(parse_error, "Invalid filter definition: unexpected character"); + } + + if (negated) + on_negation(); + + done = true; + + break; + } + + if (negation_present && !done) + { + // This would happen if a filter consists of a single '!' + BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to nothingness"); + } + + begin = p; + } + + //! Parses filtering relation + iterator_type parse_relation(iterator_type begin, iterator_type end) + { + iterator_type p = begin; + if (p != end) + { + iterator_type next = p; + if (scan_keyword(p, end, next, constants::equal_keyword())) + m_ComparisonRelation = &filter_factory_type::on_equality_relation; + else if (scan_keyword(p, end, next, constants::not_equal_keyword())) + m_ComparisonRelation = &filter_factory_type::on_inequality_relation; + else if (scan_keyword(p, end, next, constants::greater_keyword())) + m_ComparisonRelation = &filter_factory_type::on_greater_relation; + else if (scan_keyword(p, end, next, constants::less_keyword())) + m_ComparisonRelation = &filter_factory_type::on_less_relation; + else if (scan_keyword(p, end, next, constants::greater_or_equal_keyword())) + m_ComparisonRelation = &filter_factory_type::on_greater_or_equal_relation; + else if (scan_keyword(p, end, next, constants::less_or_equal_keyword())) + m_ComparisonRelation = &filter_factory_type::on_less_or_equal_relation; + else + { + // Check for custom relation + while (next != end && (encoding::isalnum(*next) || *next == constants::char_underline)) + ++next; + if (p == next) + goto DoneL; + m_CustomRelation.assign(p, next); + } + + // We have parsed a relation operator, there must be an operand + next = constants::trim_spaces_left(next, end); + string_type operand; + p = constants::parse_operand(next, end, operand); + if (next == p) + BOOST_LOG_THROW_DESCR(parse_error, "Missing operand for a relation in the filter string"); + + m_Operand = boost::in_place(operand); + } + + DoneL: + // The relation can be as simple as a sole attribute placeholder (which means that only attribute presence has to be checked). + // So regardless how we get here, the relation is parsed completely. + on_relation_complete(); + + return p; + } + + //! Checks if the string contains a keyword + static bool scan_keyword(iterator_type begin, iterator_type end, iterator_type& next, iterator_type keyword) + { + for (iterator_type p = begin; p != end; ++p, ++keyword) + { + char_type c1 = *p, c2 = *keyword; + if (c2 == 0) + { + if (encoding::isspace(c1)) + { + next = p; + return true; + } + break; + } + if (c1 != c2) + break; + } + + return false; + } + + //! The attribute name handler + void on_attribute_name(iterator_type begin, iterator_type end) + { + if (begin == end) + BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered"); + m_AttributeName = attribute_name(log::aux::to_narrow(string_type(begin, end))); + } + + //! The comparison relation handler + void on_relation_complete() + { + if (!!m_AttributeName) + { + filters_repository< char_type > const& repo = filters_repository< char_type >::get(); + filter_factory_type& factory = repo.get_factory(m_AttributeName); + + if (!!m_Operand) + { + if (!!m_ComparisonRelation) + { + m_Subexpressions.push((factory.*m_ComparisonRelation)(m_AttributeName, m_Operand.get())); + m_ComparisonRelation = NULL; + } + else if (!m_CustomRelation.empty()) + { + m_Subexpressions.push(factory.on_custom_relation(m_AttributeName, m_CustomRelation, m_Operand.get())); + m_CustomRelation.clear(); + } + else + { + // This should never happen + BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a relation"); + BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name or subexpression operation is not set while trying to construct a subexpression"); + } + + m_Operand = none; + } + else + { + // This branch is taken if the relation is a single attribute name, which is recognized as the attribute presence check + BOOST_ASSERT_MSG(!m_ComparisonRelation && m_CustomRelation.empty(), "Filter parser internal error: the relation operation is set while operand is not"); + m_Subexpressions.push(factory.on_exists_test(m_AttributeName)); + } + + m_AttributeName = attribute_name(); + } + else + { + // This should never happen + BOOST_ASSERT_MSG(false, "Filter parser internal error: the attribute name is not set while trying to construct a relation"); + BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the attribute name is not set while trying to construct a relation"); + } + } + + //! The negation operation handler + void on_negation() + { + if (!m_Subexpressions.empty()) + { + m_Subexpressions.top() = !phoenix::bind(m_Subexpressions.top(), phoenix::placeholders::_1); + } + else + { + // This would happen if a filter consists of "!()" + BOOST_LOG_THROW_DESCR(parse_error, "Filter parsing error: negation operator applied to an empty subexpression"); + } + } + + //! The logical AND operation handler + void on_and() + { + if (!m_Subexpressions.empty()) + { + filter right = boost::move(m_Subexpressions.top()); + m_Subexpressions.pop(); + if (!m_Subexpressions.empty()) + { + filter const& left = m_Subexpressions.top(); + m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) && phoenix::bind(right, phoenix::placeholders::_1); + return; + } + } + + // This should never happen + BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter"); + } + + //! The logical OR operation handler + void on_or() + { + if (!m_Subexpressions.empty()) + { + filter right = boost::move(m_Subexpressions.top()); + m_Subexpressions.pop(); + if (!m_Subexpressions.empty()) + { + filter const& left = m_Subexpressions.top(); + m_Subexpressions.top() = phoenix::bind(left, phoenix::placeholders::_1) || phoenix::bind(right, phoenix::placeholders::_1); + return; + } + } + + // This should never happen + BOOST_LOG_THROW_DESCR(parse_error, "Filter parser internal error: the subexpression is not set while trying to construct a filter"); + } + + // Assignment and copying are prohibited + BOOST_DELETED_FUNCTION(filter_parser(filter_parser const&)) + BOOST_DELETED_FUNCTION(filter_parser& operator= (filter_parser const&)) +}; + +} // namespace + +//! The function registers a filter factory object for the specified attribute name +template< typename CharT > +BOOST_LOG_SETUP_API void register_filter_factory(attribute_name const& name, shared_ptr< filter_factory< CharT > > const& factory) +{ + BOOST_ASSERT(!!name); + BOOST_ASSERT(!!factory); + + filters_repository< CharT >& repo = filters_repository< CharT >::get(); + + BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) + repo.m_Map[name] = factory; +} + +//! The function parses a filter from the string +template< typename CharT > +BOOST_LOG_SETUP_API filter parse_filter(const CharT* begin, const CharT* end) +{ + typedef CharT char_type; + + filter_parser< char_type > parser; + const char_type* p = begin; + + BOOST_LOG_EXPR_IF_MT(filters_repository< CharT >& repo = filters_repository< CharT >::get();) + BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) + + parser.parse(p, end); + + return parser.get_filter(); +} + +#ifdef BOOST_LOG_USE_CHAR + +template BOOST_LOG_SETUP_API +void register_filter_factory< char >(attribute_name const& name, shared_ptr< filter_factory< char > > const& factory); +template BOOST_LOG_SETUP_API +filter parse_filter< char >(const char* begin, const char* end); + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +template BOOST_LOG_SETUP_API +void register_filter_factory< wchar_t >(attribute_name const& name, shared_ptr< filter_factory< wchar_t > > const& factory); +template BOOST_LOG_SETUP_API +filter parse_filter< wchar_t >(const wchar_t* begin, const wchar_t* end); + +#endif // BOOST_LOG_USE_WCHAR_T + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS diff --git a/src/boost/libs/log/src/setup/formatter_parser.cpp b/src/boost/libs/log/src/setup/formatter_parser.cpp new file mode 100644 index 00000000..09ab8ee6 --- /dev/null +++ b/src/boost/libs/log/src/setup/formatter_parser.cpp @@ -0,0 +1,486 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 formatter_parser.cpp + * \author Andrey Semashev + * \date 07.04.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS + +#include <boost/log/detail/setup_config.hpp> +#include <map> +#include <string> +#include <sstream> +#include <stdexcept> +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/move/core.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/optional/optional.hpp> +#include <boost/utility/in_place_factory.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/detail/default_attribute_names.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/utility/functional/nop.hpp> +#include <boost/log/utility/setup/formatter_parser.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/log/detail/locks.hpp> +#include <boost/log/detail/light_rw_mutex.hpp> +#endif +#include "parser_utils.hpp" +#include "spirit_encoding.hpp" +#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) +#include "default_formatter_factory.hpp" +#endif +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! The structure contains formatter factories repository +template< typename CharT > +struct formatters_repository : + public log::aux::lazy_singleton< formatters_repository< CharT > > +{ + //! Base class type + typedef log::aux::lazy_singleton< formatters_repository< CharT > > base_type; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) + friend class log::aux::lazy_singleton< formatters_repository< CharT > >; +#else + friend class base_type; +#endif + + typedef CharT char_type; + typedef formatter_factory< char_type > formatter_factory_type; + + //! Attribute name ordering predicate + struct attribute_name_order + { + typedef bool result_type; + result_type operator() (attribute_name const& left, attribute_name const& right) const + { + return left.id() < right.id(); + } + }; + + //! Map of formatter factories + typedef std::map< attribute_name, shared_ptr< formatter_factory_type >, attribute_name_order > factories_map; + + +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + mutable log::aux::light_rw_mutex m_Mutex; +#endif + //! The map of formatter factories + factories_map m_Map; +#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + //! Default factory + mutable aux::default_formatter_factory< char_type > m_DefaultFactory; +#endif + + //! The method returns the filter factory for the specified attribute name + formatter_factory_type& get_factory(attribute_name const& name) const + { + typename factories_map::const_iterator it = m_Map.find(name); + if (it != m_Map.end()) + { + return *it->second; + } + else + { +#if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + return m_DefaultFactory; +#else + BOOST_LOG_THROW_DESCR(setup_error, "No formatter factory registered for attribute " + name.string()); +#endif + } + } + +private: + formatters_repository() + { + } +}; + +//! Function object for formatter chaining +template< typename CharT, typename SecondT > +struct chained_formatter +{ + typedef void result_type; + typedef basic_formatter< CharT > formatter_type; + typedef typename formatter_type::stream_type stream_type; + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + explicit chained_formatter(formatter_type&& first, SecondT&& second) : +#else + template< typename T > + explicit chained_formatter(BOOST_RV_REF(formatter_type) first, T const& second) : +#endif + m_first(boost::move(first)), m_second(boost::move(second)) + { + } + + result_type operator() (record_view const& rec, stream_type& strm) const + { + m_first(rec, strm); + m_second(rec, strm); + } + +private: + formatter_type m_first; + SecondT m_second; +}; + +//! String literal formatter +template< typename CharT > +struct literal_formatter +{ + typedef void result_type; + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef basic_formatting_ostream< char_type > stream_type; + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + explicit literal_formatter(string_type&& str) : m_str(boost::move(str)) +#else + explicit literal_formatter(string_type const& str) : m_str(str) +#endif + { + } + + result_type operator() (record_view const& rec, stream_type& strm) const + { + strm << m_str; + } + +private: + const string_type m_str; +}; + +//! Formatter parsing grammar +template< typename CharT > +class formatter_parser +{ +private: + typedef CharT char_type; + typedef const char_type* iterator_type; + typedef std::basic_string< char_type > string_type; + typedef basic_formatter< char_type > formatter_type; + typedef boost::log::aux::char_constants< char_type > constants; + typedef typename log::aux::encoding< char_type >::type encoding; + typedef log::aux::encoding_specific< encoding > encoding_specific; + typedef formatter_factory< char_type > formatter_factory_type; + typedef typename formatter_factory_type::args_map args_map; + +private: + //! The formatter being constructed + optional< formatter_type > m_Formatter; + + //! Attribute name + attribute_name m_AttrName; + //! Formatter factory arguments + args_map m_FactoryArgs; + + //! Formatter argument name + mutable string_type m_ArgName; + //! Argument value + mutable string_type m_ArgValue; + +public: + //! Constructor + formatter_parser() + { + } + + //! Parses formatter + void parse(iterator_type& begin, iterator_type end) + { + iterator_type p = begin; + + while (p != end) + { + // Find the end of a string literal + iterator_type start = p; + for (; p != end; ++p) + { + char_type c = *p; + if (c == constants::char_backslash) + { + // We found an escaped character + ++p; + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the formatter string"); + } + else if (c == constants::char_percent) + { + // We found an attribute + break; + } + } + + if (start != p) + push_string(start, p); + + if (p != end) + { + // We found an attribute placeholder + iterator_type start = constants::trim_spaces_left(++p, end); + p = constants::scan_attr_placeholder(start, end); + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string"); + + on_attribute_name(start, p); + + p = constants::trim_spaces_left(p, end); + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string"); + + if (*p == constants::char_paren_bracket_left) + { + // We found formatter arguments + p = parse_args(constants::trim_spaces_left(++p, end), end); + p = constants::trim_spaces_left(p, end); + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string"); + } + + if (*p != constants::char_percent) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string"); + + ++p; + + push_attr(); + } + } + + begin = p; + } + + //! Returns the parsed formatter + formatter_type get_formatter() + { + if (!m_Formatter) + { + // This may happen if parser input is an empty string + return formatter_type(nop()); + } + + return boost::move(m_Formatter.get()); + } + +private: + //! The method parses formatter arguments + iterator_type parse_args(iterator_type begin, iterator_type end) + { + iterator_type p = begin; + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string"); + if (*p == constants::char_paren_bracket_right) + return ++p; + + while (true) + { + char_type c = *p; + + // Read argument name + iterator_type start = p; + if (!encoding::isalpha(*p)) + BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid"); + for (++p; p != end; ++p) + { + c = *p; + if (encoding::isspace(c) || c == constants::char_equal) + break; + if (!encoding::isalnum(c) && c != constants::char_underline) + BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid"); + } + + if (start == p) + BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is empty"); + + on_arg_name(start, p); + + p = constants::trim_spaces_left(p, end); + if (p == end || *p != constants::char_equal) + BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument description is not valid"); + + // Read argument value + start = p = constants::trim_spaces_left(++p, end); + p = constants::parse_operand(p, end, m_ArgValue); + if (p == start) + BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument value is not specified"); + + push_arg(); + + p = constants::trim_spaces_left(p, end); + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string"); + + c = *p; + if (c == constants::char_paren_bracket_right) + { + break; + } + else if (c == constants::char_comma) + { + p = constants::trim_spaces_left(++p, end); + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid"); + } + else + { + BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string"); + } + } + + return ++p; + } + + //! The method is called when an argument name is discovered + void on_arg_name(iterator_type begin, iterator_type end) + { + m_ArgName.assign(begin, end); + } + + //! The method is called when an argument is filled + void push_arg() + { + m_FactoryArgs[m_ArgName] = m_ArgValue; + m_ArgName.clear(); + m_ArgValue.clear(); + } + + //! The method is called when an attribute name is discovered + void on_attribute_name(iterator_type begin, iterator_type end) + { + if (begin == end) + BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered"); + + // For compatibility with Boost.Log v1 we recognize %_% as the message attribute name + const std::size_t len = end - begin; + if (std::char_traits< char_type >::length(constants::message_text_keyword()) == len && + std::char_traits< char_type >::compare(constants::message_text_keyword(), begin, len) == 0) + { + m_AttrName = log::aux::default_attribute_names::message(); + } + else + { + m_AttrName = attribute_name(log::aux::to_narrow(string_type(begin, end))); + } + } + //! The method is called when an attribute is filled + void push_attr() + { + BOOST_ASSERT_MSG(!!m_AttrName, "Attribute name is not set"); + + if (m_AttrName == log::aux::default_attribute_names::message()) + { + // We make a special treatment for the message text formatter + append_formatter(expressions::aux::message_formatter()); + } + else + { + // Use the factory to create the formatter + formatters_repository< char_type > const& repo = formatters_repository< char_type >::get(); + formatter_factory_type& factory = repo.get_factory(m_AttrName); + append_formatter(factory.create_formatter(m_AttrName, m_FactoryArgs)); + } + + // Eventually, clear all the auxiliary data + m_AttrName = attribute_name(); + m_FactoryArgs.clear(); + } + + //! The method is called when a string literal is discovered + void push_string(iterator_type begin, iterator_type end) + { + string_type s(begin, end); + constants::translate_escape_sequences(s); + append_formatter(literal_formatter< char_type >(boost::move(s))); + } + + //! The method appends a formatter part to the final formatter + template< typename FormatterT > + void append_formatter(FormatterT fmt) + { + if (!!m_Formatter) + m_Formatter = boost::in_place(chained_formatter< char_type, FormatterT >(boost::move(m_Formatter.get()), boost::move(fmt))); + else + m_Formatter = boost::in_place(boost::move(fmt)); + } + + // Assignment and copying are prohibited + BOOST_DELETED_FUNCTION(formatter_parser(formatter_parser const&)) + BOOST_DELETED_FUNCTION(formatter_parser& operator= (formatter_parser const&)) +}; + +} // namespace + +//! The function registers a user-defined formatter factory +template< typename CharT > +BOOST_LOG_SETUP_API void register_formatter_factory(attribute_name const& name, shared_ptr< formatter_factory< CharT > > const& factory) +{ + BOOST_ASSERT(!!name); + BOOST_ASSERT(!!factory); + + formatters_repository< CharT >& repo = formatters_repository< CharT >::get(); + BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) + repo.m_Map[name] = factory; +} + +//! The function parses a formatter from the string +template< typename CharT > +BOOST_LOG_SETUP_API basic_formatter< CharT > parse_formatter(const CharT* begin, const CharT* end) +{ + typedef CharT char_type; + + formatter_parser< char_type > parser; + const char_type* p = begin; + + BOOST_LOG_EXPR_IF_MT(formatters_repository< CharT >& repo = formatters_repository< CharT >::get();) + BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) + + parser.parse(p, end); + + return parser.get_formatter(); +} + +#ifdef BOOST_LOG_USE_CHAR +template BOOST_LOG_SETUP_API +void register_formatter_factory< char >( + attribute_name const& attr_name, shared_ptr< formatter_factory< char > > const& factory); +template BOOST_LOG_SETUP_API +basic_formatter< char > parse_formatter< char >(const char* begin, const char* end); +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_SETUP_API +void register_formatter_factory< wchar_t >( + attribute_name const& attr_name, shared_ptr< formatter_factory< wchar_t > > const& factory); +template BOOST_LOG_SETUP_API +basic_formatter< wchar_t > parse_formatter< wchar_t >(const wchar_t* begin, const wchar_t* end); +#endif // BOOST_LOG_USE_WCHAR_T + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS diff --git a/src/boost/libs/log/src/setup/init_from_settings.cpp b/src/boost/libs/log/src/setup/init_from_settings.cpp new file mode 100644 index 00000000..d8da54cd --- /dev/null +++ b/src/boost/libs/log/src/setup/init_from_settings.cpp @@ -0,0 +1,886 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 init_from_settings.cpp + * \author Andrey Semashev + * \date 11.10.2009 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS + +#if defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \ + && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 +// This warning is caused by a compiler bug which is exposed when boost::optional is used: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679 +// It has to be disabled here, before any code is included, since otherwise it doesn't help and the warning is still emitted. +// '*((void*)& foo +2)' may be used uninitialized in this function +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#include <boost/log/detail/setup_config.hpp> +#include <cstddef> +#include <ios> +#include <map> +#include <vector> +#include <string> +#include <utility> +#include <iostream> +#include <typeinfo> +#include <stdexcept> +#include <algorithm> +#include <boost/type.hpp> +#include <boost/bind.hpp> +#include <boost/limits.hpp> +#include <boost/cstdint.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/core/null_deleter.hpp> +#include <boost/optional/optional.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/date_time/date_defs.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/mpl/if.hpp> +#include <boost/type_traits/is_unsigned.hpp> +#include <boost/spirit/home/qi/numeric/numeric_utils.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/detail/default_attribute_names.hpp> +#include <boost/log/core.hpp> +#include <boost/log/sinks.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/sinks/auto_newline_mode.hpp> +#include <boost/log/sinks/frontend_requirements.hpp> +#include <boost/log/expressions/filter.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/utility/functional/nop.hpp> +#include <boost/log/utility/setup/from_settings.hpp> +#include <boost/log/utility/setup/filter_parser.hpp> +#include <boost/log/utility/setup/formatter_parser.hpp> +#if !defined(BOOST_LOG_NO_ASIO) +#include <boost/asio/ip/address.hpp> +#endif +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/log/detail/locks.hpp> +#include <boost/log/detail/light_rw_mutex.hpp> +#endif +#include "parser_utils.hpp" +#include "spirit_encoding.hpp" +#include <boost/log/detail/header.hpp> + +namespace qi = boost::spirit::qi; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! Throws an exception when a parameter value is not valid +BOOST_LOG_NORETURN void throw_invalid_value(const char* param_name) +{ + std::string descr = std::string("Invalid parameter \"") + + param_name + + "\" value"; + BOOST_LOG_THROW_DESCR(invalid_value, descr); +} + +//! Extracts an integral value from parameter value +template< typename IntT, typename CharT > +inline IntT param_cast_to_int(const char* param_name, std::basic_string< CharT > const& value) +{ + IntT res = 0; + typedef typename mpl::if_< + is_unsigned< IntT >, + qi::extract_uint< IntT, 10, 1, -1 >, + qi::extract_int< IntT, 10, 1, -1 > + >::type extract; + const CharT* begin = value.c_str(), *end = begin + value.size(); + if (extract::call(begin, end, res) && begin == end) + return res; + else + throw_invalid_value(param_name); +} + +//! Case-insensitive character comparison predicate +struct is_case_insensitive_equal +{ + typedef bool result_type; + + template< typename CharT > + result_type operator() (CharT left, CharT right) const BOOST_NOEXCEPT + { + typedef typename boost::log::aux::encoding< CharT >::type encoding; + return encoding::tolower(left) == encoding::tolower(right); + } +}; + +//! Extracts a boolean value from parameter value +template< typename CharT > +inline bool param_cast_to_bool(const char* param_name, std::basic_string< CharT > const& value) +{ + typedef CharT char_type; + typedef boost::log::aux::char_constants< char_type > constants; + typedef boost::log::basic_string_literal< char_type > literal_type; + + const char_type* begin = value.c_str(), *end = begin + value.size(); + std::size_t len = end - begin; + + literal_type keyword = constants::true_keyword(); + if (keyword.size() == len && std::equal(begin, end, keyword.c_str(), is_case_insensitive_equal())) + { + return true; + } + else + { + keyword = constants::false_keyword(); + if (keyword.size() == len && std::equal(begin, end, keyword.c_str(), is_case_insensitive_equal())) + { + return false; + } + else + { + return param_cast_to_int< unsigned int >(param_name, value) != 0; + } + } +} + +//! Extracts an \c auto_newline_mode value from parameter value +template< typename CharT > +inline sinks::auto_newline_mode param_cast_to_auto_newline_mode(const char* param_name, std::basic_string< CharT > const& value) +{ + typedef CharT char_type; + typedef boost::log::aux::char_constants< char_type > constants; + + if (value == constants::auto_newline_mode_disabled()) + return sinks::disabled_auto_newline; + else if (value == constants::auto_newline_mode_always_insert()) + return sinks::always_insert; + else if (value == constants::auto_newline_mode_insert_if_missing()) + return sinks::insert_if_missing; + else + { + BOOST_LOG_THROW_DESCR(invalid_value, + "Auto newline mode \"" + boost::log::aux::to_narrow(value) + "\" is not supported"); + } +} + +#if !defined(BOOST_LOG_NO_ASIO) +//! Extracts a network address from parameter value +template< typename CharT > +inline std::string param_cast_to_address(const char* param_name, std::basic_string< CharT > const& value) +{ + return log::aux::to_narrow(value); +} +#endif // !defined(BOOST_LOG_NO_ASIO) + +template< typename CharT > +inline bool is_weekday(const CharT* str, std::size_t len, boost::log::basic_string_literal< CharT > const& weekday, boost::log::basic_string_literal< CharT > const& short_weekday) +{ + return (len == weekday.size() && std::equal(weekday.begin(), weekday.end(), str)) || + (len == short_weekday.size() && std::equal(short_weekday.begin(), short_weekday.end(), str)); +} + +//! The function extracts the file rotation time point predicate from the parameter +template< typename CharT > +sinks::file::rotation_at_time_point param_cast_to_rotation_time_point(const char* param_name, std::basic_string< CharT > const& value) +{ + typedef CharT char_type; + typedef boost::log::aux::char_constants< char_type > constants; + typedef typename boost::log::aux::encoding< char_type >::type encoding; + typedef qi::extract_uint< unsigned short, 10, 1, 2 > day_extract; + typedef qi::extract_uint< unsigned char, 10, 2, 2 > time_component_extract; + + const char_type colon = static_cast< char_type >(':'); + optional< date_time::weekdays > weekday; + optional< unsigned short > day; + unsigned char hour = 0, minute = 0, second = 0; + const char_type* begin = value.c_str(), *end = begin + value.size(); + + if (!encoding::isalnum(*begin)) // begin is null-terminated, so we also check that the string is not empty here + throw_invalid_value(param_name); + + const char_type* p = begin + 1; + if (encoding::isalpha(*begin)) + { + // This must be a weekday + while (encoding::isalpha(*p)) + ++p; + + std::size_t len = p - begin; + if (is_weekday(begin, len, constants::monday_keyword(), constants::short_monday_keyword())) + weekday = date_time::Monday; + else if (is_weekday(begin, len, constants::tuesday_keyword(), constants::short_tuesday_keyword())) + weekday = date_time::Tuesday; + else if (is_weekday(begin, len, constants::wednesday_keyword(), constants::short_wednesday_keyword())) + weekday = date_time::Wednesday; + else if (is_weekday(begin, len, constants::thursday_keyword(), constants::short_thursday_keyword())) + weekday = date_time::Thursday; + else if (is_weekday(begin, len, constants::friday_keyword(), constants::short_friday_keyword())) + weekday = date_time::Friday; + else if (is_weekday(begin, len, constants::saturday_keyword(), constants::short_saturday_keyword())) + weekday = date_time::Saturday; + else if (is_weekday(begin, len, constants::sunday_keyword(), constants::short_sunday_keyword())) + weekday = date_time::Sunday; + else + throw_invalid_value(param_name); + } + else + { + // This may be either a month day or an hour + while (encoding::isdigit(*p)) + ++p; + + if (encoding::isspace(*p)) + { + // This is a month day + unsigned short mday = 0; + const char_type* b = begin; + if (!day_extract::call(b, p, mday) || b != p) + throw_invalid_value(param_name); + + day = mday; + } + else if (*p == colon) + { + // This is an hour, reset the pointer + p = begin; + } + else + throw_invalid_value(param_name); + } + + // Skip spaces + while (encoding::isspace(*p)) + ++p; + + // Parse hour + if (!time_component_extract::call(p, end, hour) || *p != colon) + throw_invalid_value(param_name); + ++p; + + // Parse minute + if (!time_component_extract::call(p, end, minute) || *p != colon) + throw_invalid_value(param_name); + ++p; + + // Parse second + if (!time_component_extract::call(p, end, second) || p != end) + throw_invalid_value(param_name); + + // Construct the predicate + if (weekday) + return sinks::file::rotation_at_time_point(weekday.get(), hour, minute, second); + else if (day) + return sinks::file::rotation_at_time_point(gregorian::greg_day(day.get()), hour, minute, second); + else + return sinks::file::rotation_at_time_point(hour, minute, second); +} + +//! Base class for default sink factories +template< typename CharT > +class basic_default_sink_factory : + public sink_factory< CharT > +{ +public: + typedef sink_factory< CharT > base_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::settings_section settings_section; + typedef boost::log::aux::char_constants< char_type > constants; + +protected: + //! Sink backend character selection function + template< typename InitializerT > + static shared_ptr< sinks::sink > select_backend_character_type(settings_section const& params, InitializerT initializer) + { +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + if (optional< string_type > wide_param = params["Wide"]) + { + if (param_cast_to_bool("Wide", wide_param.get())) + return initializer(params, type< wchar_t >()); + } + + return initializer(params, type< char >()); +#elif defined(BOOST_LOG_USE_CHAR) + return initializer(params, type< char >()); +#elif defined(BOOST_LOG_USE_WCHAR_T) + return initializer(params, type< wchar_t >()); +#endif + } + + //! The function initializes common parameters of a formatting sink and returns the constructed sink + template< typename BackendT > + static shared_ptr< sinks::sink > init_sink(shared_ptr< BackendT > const& backend, settings_section const& params) + { + typedef BackendT backend_t; + typedef typename sinks::has_requirement< + typename backend_t::frontend_requirements, + sinks::formatted_records + >::type is_formatting_t; + + // Filter + filter filt; + if (optional< string_type > filter_param = params["Filter"]) + { + filt = parse_filter(filter_param.get()); + } + + shared_ptr< sinks::basic_sink_frontend > p; + +#if !defined(BOOST_LOG_NO_THREADS) + // Asynchronous. TODO: make it more flexible. + bool async = false; + if (optional< string_type > async_param = params["Asynchronous"]) + { + async = param_cast_to_bool("Asynchronous", async_param.get()); + } + + // Construct the frontend, considering Asynchronous parameter + if (!async) + { + p = init_formatter(boost::make_shared< sinks::synchronous_sink< backend_t > >(backend), params, is_formatting_t()); + } + else + { + p = init_formatter(boost::make_shared< sinks::asynchronous_sink< backend_t > >(backend), params, is_formatting_t()); + + // https://svn.boost.org/trac/boost/ticket/10638 + // The user doesn't have a way to process excaptions from the dedicated thread anyway, so just suppress them instead of + // terminating the application. + p->set_exception_handler(nop()); + } +#else + // When multithreading is disabled we always use the unlocked sink frontend + p = init_formatter(boost::make_shared< sinks::unlocked_sink< backend_t > >(backend), params, is_formatting_t()); +#endif + + p->set_filter(filt); + + return p; + } + +private: + //! The function initializes formatter for the sinks that support formatting + template< typename SinkT > + static shared_ptr< SinkT > init_formatter(shared_ptr< SinkT > const& sink, settings_section const& params, mpl::true_) + { + // Formatter + if (optional< string_type > format_param = params["Format"]) + { + typedef typename SinkT::char_type sink_char_type; + std::basic_string< sink_char_type > format_str; + log::aux::code_convert(format_param.get(), format_str); + sink->set_formatter(parse_formatter(format_str)); + } + return sink; + } + template< typename SinkT > + static shared_ptr< SinkT > init_formatter(shared_ptr< SinkT > const& sink, settings_section const& params, mpl::false_) + { + return sink; + } +}; + +//! Default console sink factory +template< typename CharT > +class default_console_sink_factory : + public basic_default_sink_factory< CharT > +{ +public: + typedef basic_default_sink_factory< CharT > base_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::settings_section settings_section; + typedef typename base_type::constants constants; + +private: + struct impl; + friend struct impl; + struct impl + { + typedef shared_ptr< sinks::sink > result_type; + + template< typename BackendCharT > + result_type operator() (settings_section const& params, type< BackendCharT >) const + { + // Construct the backend + typedef boost::log::aux::char_constants< BackendCharT > constants; + typedef sinks::basic_text_ostream_backend< BackendCharT > backend_t; + shared_ptr< backend_t > backend = boost::make_shared< backend_t >(); + backend->add_stream(shared_ptr< typename backend_t::stream_type >(&constants::get_console_log_stream(), boost::null_deleter())); + + // Auto newline mode + if (optional< string_type > auto_newline_param = params["AutoNewline"]) + { + backend->set_auto_newline_mode(param_cast_to_auto_newline_mode("AutoNewline", auto_newline_param.get())); + } + + // Auto flush + if (optional< string_type > auto_flush_param = params["AutoFlush"]) + { + backend->auto_flush(param_cast_to_bool("AutoFlush", auto_flush_param.get())); + } + + return base_type::init_sink(backend, params); + } + }; + +public: + //! The function constructs a sink that writes log records to the console + shared_ptr< sinks::sink > create_sink(settings_section const& params) + { + return base_type::select_backend_character_type(params, impl()); + } +}; + +//! Default text file sink factory +template< typename CharT > +class default_text_file_sink_factory : + public basic_default_sink_factory< CharT > +{ +public: + typedef basic_default_sink_factory< CharT > base_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::settings_section settings_section; + typedef typename base_type::constants constants; + +public: + //! The function constructs a sink that writes log records to a text file + shared_ptr< sinks::sink > create_sink(settings_section const& params) + { + typedef sinks::text_file_backend backend_t; + shared_ptr< backend_t > backend = boost::make_shared< backend_t >(); + + // FileName + if (optional< string_type > file_name_param = params["FileName"]) + { + backend->set_file_name_pattern(filesystem::path(file_name_param.get())); + } + else + BOOST_LOG_THROW_DESCR(missing_value, "File name is not specified"); + + // Target file name + if (optional< string_type > target_file_name_param = params["TargetFileName"]) + { + backend->set_target_file_name_pattern(filesystem::path(target_file_name_param.get())); + } + + // File rotation size + if (optional< string_type > rotation_size_param = params["RotationSize"]) + { + backend->set_rotation_size(param_cast_to_int< uintmax_t >("RotationSize", rotation_size_param.get())); + } + + // File rotation interval + if (optional< string_type > rotation_interval_param = params["RotationInterval"]) + { + backend->set_time_based_rotation(sinks::file::rotation_at_time_interval( + posix_time::seconds(param_cast_to_int< unsigned int >("RotationInterval", rotation_interval_param.get())))); + } + else if (optional< string_type > rotation_time_point_param = params["RotationTimePoint"]) + { + // File rotation time point + backend->set_time_based_rotation(param_cast_to_rotation_time_point("RotationTimePoint", rotation_time_point_param.get())); + } + + // Final rotation + if (optional< string_type > enable_final_rotation_param = params["EnableFinalRotation"]) + { + backend->enable_final_rotation(param_cast_to_bool("EnableFinalRotation", enable_final_rotation_param.get())); + } + + // Auto newline mode + if (optional< string_type > auto_newline_param = params["AutoNewline"]) + { + backend->set_auto_newline_mode(param_cast_to_auto_newline_mode("AutoNewline", auto_newline_param.get())); + } + + // Auto flush + if (optional< string_type > auto_flush_param = params["AutoFlush"]) + { + backend->auto_flush(param_cast_to_bool("AutoFlush", auto_flush_param.get())); + } + + // Append + if (optional< string_type > append_param = params["Append"]) + { + if (param_cast_to_bool("Append", append_param.get())) + backend->set_open_mode(std::ios_base::out | std::ios_base::app); + } + + // File collector parameters + // Target directory + if (optional< string_type > target_param = params["Target"]) + { + filesystem::path target_dir(target_param.get()); + + // Max total size + uintmax_t max_size = (std::numeric_limits< uintmax_t >::max)(); + if (optional< string_type > max_size_param = params["MaxSize"]) + max_size = param_cast_to_int< uintmax_t >("MaxSize", max_size_param.get()); + + // Min free space + uintmax_t space = 0; + if (optional< string_type > min_space_param = params["MinFreeSpace"]) + space = param_cast_to_int< uintmax_t >("MinFreeSpace", min_space_param.get()); + + // Max number of files + uintmax_t max_files = (std::numeric_limits< uintmax_t >::max)(); + if (optional< string_type > max_files_param = params["MaxFiles"]) + max_files = param_cast_to_int< uintmax_t >("MaxFiles", max_files_param.get()); + + backend->set_file_collector(sinks::file::make_collector( + keywords::target = target_dir, + keywords::max_size = max_size, + keywords::min_free_space = space, + keywords::max_files = max_files)); + + // Scan for log files + if (optional< string_type > scan_param = params["ScanForFiles"]) + { + string_type const& value = scan_param.get(); + if (value == constants::scan_method_all()) + backend->scan_for_files(sinks::file::scan_all); + else if (value == constants::scan_method_matching()) + backend->scan_for_files(sinks::file::scan_matching); + else + { + BOOST_LOG_THROW_DESCR(invalid_value, + "File scan method \"" + boost::log::aux::to_narrow(value) + "\" is not supported"); + } + } + } + + return base_type::init_sink(backend, params); + } +}; + +#ifndef BOOST_LOG_WITHOUT_SYSLOG + +//! Default syslog sink factory +template< typename CharT > +class default_syslog_sink_factory : + public basic_default_sink_factory< CharT > +{ +public: + typedef basic_default_sink_factory< CharT > base_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::settings_section settings_section; + typedef typename base_type::constants constants; + +public: + //! The function constructs a sink that writes log records to syslog + shared_ptr< sinks::sink > create_sink(settings_section const& params) + { + // Construct the backend + typedef sinks::syslog_backend backend_t; + shared_ptr< backend_t > backend = boost::make_shared< backend_t >(); + + // For now we use only the default level mapping. Will add support for configuration later. + backend->set_severity_mapper(sinks::syslog::direct_severity_mapping< >(log::aux::default_attribute_names::severity())); + +#if !defined(BOOST_LOG_NO_ASIO) + // Setup local and remote addresses + if (optional< string_type > local_address_param = params["LocalAddress"]) + backend->set_local_address(param_cast_to_address("LocalAddress", local_address_param.get())); + + if (optional< string_type > target_address_param = params["TargetAddress"]) + backend->set_target_address(param_cast_to_address("TargetAddress", target_address_param.get())); +#endif // !defined(BOOST_LOG_NO_ASIO) + + return base_type::init_sink(backend, params); + } +}; + +#endif // !defined(BOOST_LOG_WITHOUT_SYSLOG) + +#ifndef BOOST_LOG_WITHOUT_DEBUG_OUTPUT + +//! Default debugger sink factory +template< typename CharT > +class default_debugger_sink_factory : + public basic_default_sink_factory< CharT > +{ +public: + typedef basic_default_sink_factory< CharT > base_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::settings_section settings_section; + typedef typename base_type::constants constants; + +private: + struct impl; + friend struct impl; + struct impl + { + typedef shared_ptr< sinks::sink > result_type; + + template< typename BackendCharT > + result_type operator() (settings_section const& params, type< BackendCharT >) const + { + // Construct the backend + typedef sinks::basic_debug_output_backend< BackendCharT > backend_t; + shared_ptr< backend_t > backend = boost::make_shared< backend_t >(); + + return base_type::init_sink(backend, params); + } + }; + +public: + //! The function constructs a sink that writes log records to the debugger + shared_ptr< sinks::sink > create_sink(settings_section const& params) + { + return base_type::select_backend_character_type(params, impl()); + } +}; + +#endif // !defined(BOOST_LOG_WITHOUT_DEBUG_OUTPUT) + +#ifndef BOOST_LOG_WITHOUT_EVENT_LOG + +//! Default simple event log sink factory +template< typename CharT > +class default_simple_event_log_sink_factory : + public basic_default_sink_factory< CharT > +{ +public: + typedef basic_default_sink_factory< CharT > base_type; + typedef typename base_type::char_type char_type; + typedef typename base_type::string_type string_type; + typedef typename base_type::settings_section settings_section; + typedef typename base_type::constants constants; + +private: + struct impl; + friend struct impl; + struct impl + { + typedef shared_ptr< sinks::sink > result_type; + + template< typename BackendCharT > + result_type operator() (settings_section const& params, type< BackendCharT >) const + { + typedef sinks::basic_simple_event_log_backend< BackendCharT > backend_t; + typedef typename backend_t::string_type backend_string_type; + + // Determine the log name + backend_string_type log_name; + if (optional< string_type > log_name_param = params["LogName"]) + log::aux::code_convert(log_name_param.get(), log_name); + else + log_name = backend_t::get_default_log_name(); + + // Determine the log source name + backend_string_type source_name; + if (optional< string_type > log_source_param = params["LogSource"]) + log::aux::code_convert(log_source_param.get(), source_name); + else + source_name = backend_t::get_default_source_name(); + + // Determine the registration mode + sinks::event_log::registration_mode reg_mode = sinks::event_log::on_demand; + if (optional< string_type > registration_param = params["Registration"]) + { + string_type const& value = registration_param.get(); + if (value == constants::registration_never()) + reg_mode = sinks::event_log::never; + else if (value == constants::registration_on_demand()) + reg_mode = sinks::event_log::on_demand; + else if (value == constants::registration_forced()) + reg_mode = sinks::event_log::forced; + else + { + BOOST_LOG_THROW_DESCR(invalid_value, + "The registration mode \"" + log::aux::to_narrow(value) + "\" is not supported"); + } + } + + // Construct the backend + shared_ptr< backend_t > backend(boost::make_shared< backend_t >(( + keywords::log_name = log_name, + keywords::log_source = source_name, + keywords::registration = reg_mode))); + + // For now we use only the default event type mapping. Will add support for configuration later. + backend->set_event_type_mapper(sinks::event_log::direct_event_type_mapping< >(log::aux::default_attribute_names::severity())); + + return base_type::init_sink(backend, params); + } + }; + +public: + //! The function constructs a sink that writes log records to the Windows NT Event Log + shared_ptr< sinks::sink > create_sink(settings_section const& params) + { + return base_type::select_backend_character_type(params, impl()); + } +}; + +#endif // !defined(BOOST_LOG_WITHOUT_EVENT_LOG) + + +//! The supported sinks repository +template< typename CharT > +struct sinks_repository : + public log::aux::lazy_singleton< sinks_repository< CharT > > +{ + typedef log::aux::lazy_singleton< sinks_repository< CharT > > base_type; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) + friend class log::aux::lazy_singleton< sinks_repository< CharT > >; +#else + friend class base_type; +#endif + + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef basic_settings_section< char_type > settings_section; + typedef boost::log::aux::char_constants< char_type > constants; + typedef boost::shared_ptr< sink_factory< char_type > > sink_factory_ptr; + typedef std::map< std::string, sink_factory_ptr > sink_factories; + +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + log::aux::light_rw_mutex m_Mutex; +#endif + //! Map of the sink factories + sink_factories m_Factories; + + //! The function constructs a sink from the settings + shared_ptr< sinks::sink > construct_sink_from_settings(settings_section const& params) + { + typedef typename settings_section::const_reference param_const_reference; + if (param_const_reference dest_node = params["Destination"]) + { + std::string dest = log::aux::to_narrow(dest_node.get().get()); + + BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(m_Mutex);) + typename sink_factories::const_iterator it = m_Factories.find(dest); + if (it != m_Factories.end()) + { + return it->second->create_sink(params); + } + else + { + BOOST_LOG_THROW_DESCR(invalid_value, "The sink destination is not supported: " + dest); + } + } + else + { + BOOST_LOG_THROW_DESCR(missing_value, "The sink destination is not set"); + } + } + + static void init_instance() + { + sinks_repository& instance = base_type::get_instance(); + instance.m_Factories["TextFile"] = boost::make_shared< default_text_file_sink_factory< char_type > >(); + instance.m_Factories["Console"] = boost::make_shared< default_console_sink_factory< char_type > >(); +#ifndef BOOST_LOG_WITHOUT_SYSLOG + instance.m_Factories["Syslog"] = boost::make_shared< default_syslog_sink_factory< char_type > >(); +#endif +#ifndef BOOST_LOG_WITHOUT_DEBUG_OUTPUT + instance.m_Factories["Debugger"] = boost::make_shared< default_debugger_sink_factory< char_type > >(); +#endif +#ifndef BOOST_LOG_WITHOUT_EVENT_LOG + instance.m_Factories["SimpleEventLog"] = boost::make_shared< default_simple_event_log_sink_factory< char_type > >(); +#endif + } + +private: + sinks_repository() {} +}; + +//! The function applies the settings to the logging core +template< typename CharT > +void apply_core_settings(basic_settings_section< CharT > const& params) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + + core_ptr core = boost::log::core::get(); + + // Filter + if (optional< string_type > filter_param = params["Filter"]) + core->set_filter(parse_filter(filter_param.get())); + else + core->reset_filter(); + + // DisableLogging + if (optional< string_type > disable_logging_param = params["DisableLogging"]) + core->set_logging_enabled(!param_cast_to_bool("DisableLogging", disable_logging_param.get())); + else + core->set_logging_enabled(true); +} + +} // namespace + + +//! The function initializes the logging library from a settings container +template< typename CharT > +BOOST_LOG_SETUP_API void init_from_settings(basic_settings_section< CharT > const& setts) +{ + typedef basic_settings_section< CharT > section; + typedef typename section::char_type char_type; + typedef sinks_repository< char_type > sinks_repo_t; + + // Apply core settings + if (section core_params = setts["Core"]) + apply_core_settings(core_params); + + // Construct and initialize sinks + if (section sink_params = setts["Sinks"]) + { + sinks_repo_t& sinks_repo = sinks_repo_t::get(); + std::vector< shared_ptr< sinks::sink > > new_sinks; + + for (typename section::const_iterator it = sink_params.begin(), end = sink_params.end(); it != end; ++it) + { + section sink_params = *it; + + // Ignore empty sections as they are most likely individual parameters (which should not be here anyway) + if (!sink_params.empty()) + { + new_sinks.push_back(sinks_repo.construct_sink_from_settings(sink_params)); + } + } + + std::for_each(new_sinks.begin(), new_sinks.end(), boost::bind(&core::add_sink, core::get(), _1)); + } +} + + +//! The function registers a factory for a sink +template< typename CharT > +BOOST_LOG_SETUP_API void register_sink_factory(const char* sink_name, shared_ptr< sink_factory< CharT > > const& factory) +{ + sinks_repository< CharT >& repo = sinks_repository< CharT >::get(); + BOOST_LOG_EXPR_IF_MT(lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) + repo.m_Factories[sink_name] = factory; +} + +#ifdef BOOST_LOG_USE_CHAR +template BOOST_LOG_SETUP_API void register_sink_factory< char >(const char* sink_name, shared_ptr< sink_factory< char > > const& factory); +template BOOST_LOG_SETUP_API void init_from_settings< char >(basic_settings_section< char > const& setts); +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_SETUP_API void register_sink_factory< wchar_t >(const char* sink_name, shared_ptr< sink_factory< wchar_t > > const& factory); +template BOOST_LOG_SETUP_API void init_from_settings< wchar_t >(basic_settings_section< wchar_t > const& setts); +#endif + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS diff --git a/src/boost/libs/log/src/setup/init_from_stream.cpp b/src/boost/libs/log/src/setup/init_from_stream.cpp new file mode 100644 index 00000000..1bf518b7 --- /dev/null +++ b/src/boost/libs/log/src/setup/init_from_stream.cpp @@ -0,0 +1,47 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 init_from_stream.cpp + * \author Andrey Semashev + * \date 22.03.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS + +#include <boost/log/detail/setup_config.hpp> +#include <boost/log/utility/setup/from_settings.hpp> +#include <boost/log/utility/setup/settings_parser.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +//! The function initializes the logging library from a stream containing logging settings +template< typename CharT > +BOOST_LOG_SETUP_API void init_from_stream(std::basic_istream< CharT >& strm) +{ + init_from_settings(parse_settings(strm)); +} + +#ifdef BOOST_LOG_USE_CHAR +template BOOST_LOG_SETUP_API void init_from_stream< char >(std::basic_istream< char >& strm); +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_SETUP_API void init_from_stream< wchar_t >(std::basic_istream< wchar_t >& strm); +#endif + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS diff --git a/src/boost/libs/log/src/setup/matches_relation_factory.cpp b/src/boost/libs/log/src/setup/matches_relation_factory.cpp new file mode 100644 index 00000000..ba8abe26 --- /dev/null +++ b/src/boost/libs/log/src/setup/matches_relation_factory.cpp @@ -0,0 +1,274 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 matches_relation_factory.hpp + * \author Andrey Semashev + * \date 03.08.2013 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + +#include <boost/log/detail/setup_config.hpp> + +#if defined(BOOST_LOG_USE_STD_REGEX) && defined(BOOST_NO_CXX11_HDR_REGEX) +#error "Boost.Log: Cannot use std::regex because it is not supported by the standard library." +#endif + +#if !defined(BOOST_LOG_USE_BOOST_REGEX) && !defined(BOOST_LOG_USE_STD_REGEX) && !defined(BOOST_LOG_USE_BOOST_XPRESSIVE) +// Use Boost.Regex backend by default. It produces smaller executables and also has the best performance for small string matching. +// Note: This default has to be in sync with Boost.Log Jamfile.v2. +#define BOOST_LOG_USE_BOOST_REGEX +#endif + +#include <string> +#if defined(BOOST_LOG_USE_STD_REGEX) +#include <regex> +#include <boost/log/support/std_regex.hpp> +#elif defined(BOOST_LOG_USE_BOOST_REGEX) +#include <boost/regex.hpp> +#include <boost/log/support/regex.hpp> +#else +#include <boost/xpressive/xpressive_dynamic.hpp> +#include <boost/log/support/xpressive.hpp> +#endif +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/utility/functional/matches.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/detail/code_conversion.hpp> +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) +#include <boost/fusion/container/set.hpp> +#include <boost/fusion/sequence/intrinsic/at_key.hpp> +#include <boost/fusion/algorithm/iteration/for_each.hpp> +#endif +#include "default_filter_factory.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +#if defined(BOOST_LOG_USE_STD_REGEX) || defined(BOOST_LOG_USE_BOOST_REGEX) + +#if defined(BOOST_LOG_USE_STD_REGEX) +namespace regex_namespace = std; +#else +namespace regex_namespace = boost; +#endif + +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +//! A special filtering predicate that adopts the string operand to the attribute value character type +struct matches_predicate : + public matches_fun +{ + template< typename CharT > + struct initializer + { + typedef void result_type; + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + + explicit initializer(string_type const& val) : m_initializer(val) + { + } + + template< typename T > + result_type operator() (T& val) const + { + try + { + typedef typename T::value_type target_char_type; + std::basic_string< target_char_type > str; + log::aux::code_convert(m_initializer, str); + val.assign(str, T::ECMAScript | T::optimize); + } + catch (...) + { + } + } + + private: + string_type const& m_initializer; + }; + + typedef matches_fun::result_type result_type; + + template< typename CharT > + explicit matches_predicate(std::basic_string< CharT > const& operand) + { + fusion::for_each(m_operands, initializer< CharT >(operand)); + } + + template< typename T > + result_type operator() (T const& val) const + { + typedef typename T::value_type char_type; + typedef regex_namespace::basic_regex< char_type > regex_type; + return matches_fun::operator() (val, fusion::at_key< regex_type >(m_operands)); + } + +private: + fusion::set< regex_namespace::regex, regex_namespace::wregex > m_operands; +}; + +#else + +//! A special filtering predicate that adopts the string operand to the attribute value character type +template< typename CharT > +struct matches_predicate : + public matches_fun +{ + typedef typename matches_fun::result_type result_type; + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef regex_namespace::basic_regex< char_type > regex_type; + + explicit matches_predicate(string_type const& operand) : + m_operand(operand, regex_type::ECMAScript | regex_type::optimize) + { + } + + template< typename T > + result_type operator() (T const& val) const + { + return matches_fun::operator() (val, m_operand); + } + +private: + regex_type m_operand; +}; + +#endif + +#else // defined(BOOST_LOG_USE_STD_REGEX) || defined(BOOST_LOG_USE_BOOST_REGEX) + +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +//! A special filtering predicate that adopts the string operand to the attribute value character type +struct matches_predicate : + public matches_fun +{ + template< typename CharT > + struct initializer + { + typedef void result_type; + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + + explicit initializer(string_type const& val) : m_initializer(val) + { + } + + template< typename T > + result_type operator() (T& val) const + { + try + { + typedef typename T::char_type target_char_type; + std::basic_string< target_char_type > str; + log::aux::code_convert(m_initializer, str); + val = T::compile(str.c_str(), str.size(), T::ECMAScript | T::optimize); + } + catch (...) + { + } + } + + private: + string_type const& m_initializer; + }; + + typedef matches_fun::result_type result_type; + + template< typename CharT > + explicit matches_predicate(std::basic_string< CharT > const& operand) + { + fusion::for_each(m_operands, initializer< CharT >(operand)); + } + + template< typename T > + result_type operator() (T const& val) const + { + typedef typename T::value_type char_type; + typedef xpressive::basic_regex< const char_type* > regex_type; + return matches_fun::operator() (val, fusion::at_key< regex_type >(m_operands)); + } + +private: + fusion::set< xpressive::cregex, xpressive::wcregex > m_operands; +}; + +#else + +//! A special filtering predicate that adopts the string operand to the attribute value character type +template< typename CharT > +struct matches_predicate : + public matches_fun +{ + typedef typename matches_fun::result_type result_type; + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef xpressive::basic_regex< const char_type* > regex_type; + + explicit matches_predicate(string_type const& operand) : + m_operand(regex_type::compile(operand.c_str(), operand.size(), regex_type::ECMAScript | regex_type::optimize)) + { + } + + template< typename T > + result_type operator() (T const& val) const + { + return matches_fun::operator() (val, m_operand); + } + +private: + regex_type m_operand; +}; + +#endif + +#endif // defined(BOOST_LOG_USE_STD_REGEX) || defined(BOOST_LOG_USE_BOOST_REGEX) + +} // namespace + +//! The function parses the "matches" relation +template< typename CharT > +filter parse_matches_relation(attribute_name const& name, std::basic_string< CharT > const& operand) +{ +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + return predicate_wrapper< log::string_types::type, matches_predicate >(name, matches_predicate(operand)); +#else + return predicate_wrapper< std::basic_string< CharT >, matches_predicate< CharT > >(name, matches_predicate< CharT >(operand)); +#endif +} + +// Explicitly instantiate factory implementation +#ifdef BOOST_LOG_USE_CHAR +template +filter parse_matches_relation< char >(attribute_name const& name, std::basic_string< char > const& operand); +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template +filter parse_matches_relation< wchar_t >(attribute_name const& name, std::basic_string< wchar_t > const& operand); +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) diff --git a/src/boost/libs/log/src/setup/parser_utils.cpp b/src/boost/libs/log/src/setup/parser_utils.cpp new file mode 100644 index 00000000..08a5de8b --- /dev/null +++ b/src/boost/libs/log/src/setup/parser_utils.cpp @@ -0,0 +1,431 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 parser_utils.cpp + * \author Andrey Semashev + * \date 31.03.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS + +#include <boost/log/detail/setup_config.hpp> +#include <cctype> +#include <iterator> +#include <algorithm> +#include <boost/log/exceptions.hpp> +#include "parser_utils.hpp" +#include <boost/log/detail/header.hpp> +#ifdef BOOST_LOG_USE_WCHAR_T +#include <cwctype> +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#ifdef BOOST_LOG_USE_CHAR + +#ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + +const char_constants< char >::char_type char_constants< char >::char_comment; +const char_constants< char >::char_type char_constants< char >::char_comma; +const char_constants< char >::char_type char_constants< char >::char_dot; +const char_constants< char >::char_type char_constants< char >::char_quote; +const char_constants< char >::char_type char_constants< char >::char_percent; +const char_constants< char >::char_type char_constants< char >::char_exclamation; +const char_constants< char >::char_type char_constants< char >::char_and; +const char_constants< char >::char_type char_constants< char >::char_or; +const char_constants< char >::char_type char_constants< char >::char_equal; +const char_constants< char >::char_type char_constants< char >::char_greater; +const char_constants< char >::char_type char_constants< char >::char_less; +const char_constants< char >::char_type char_constants< char >::char_underline; +const char_constants< char >::char_type char_constants< char >::char_backslash; +const char_constants< char >::char_type char_constants< char >::char_section_bracket_left; +const char_constants< char >::char_type char_constants< char >::char_section_bracket_right; +const char_constants< char >::char_type char_constants< char >::char_paren_bracket_left; +const char_constants< char >::char_type char_constants< char >::char_paren_bracket_right; + +#endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + +//! Skips spaces in the beginning of the input +const char* char_constants< char >::trim_spaces_left(const char_type* begin, const char_type* end) +{ + using namespace std; + while (begin != end && isspace(*begin)) + ++begin; + return begin; +} + +//! Skips spaces in the end of the input +const char* char_constants< char >::trim_spaces_right(const char_type* begin, const char_type* end) +{ + using namespace std; + while (begin != end && isspace(*(end - 1))) + --end; + return end; +} + +//! Scans for the attribute name placeholder in the input +const char* char_constants< char >::scan_attr_placeholder(const char_type* begin, const char_type* end) +{ + using namespace std; + while (begin != end) + { + char_type c = *begin; + if (!isalnum(c) && c != char_underline) + break; + ++begin; + } + + return begin; +} + +//! Parses an operand string (possibly quoted) from the input +const char* char_constants< char >::parse_operand(const char_type* begin, const char_type* end, string_type& operand) +{ + using namespace std; // to make sure we can use C functions unqualified + + const char_type* p = begin; + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Operand value is empty"); + + char_type c = *p; + if (c == char_quote) + { + // The value is specified as a quoted string + const char_type* start = ++p; + for (; p != end; ++p) + { + c = *p; + if (c == char_quote) + { + break; + } + else if (c == char_backslash) + { + ++p; + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the argument value"); + } + } + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Unterminated quoted string in the argument value"); + + operand.assign(start, p); + translate_escape_sequences(operand); + + ++p; // skip the closing quote + } + else + { + // The value is specified as a single word + const char_type* start = p; + for (++p; p != end; ++p) + { + c = *p; + if (!isalnum(c) && c != '_' && c != '-' && c != '+' && c != '.') + break; + } + + operand.assign(start, p); + } + + return p; +} + +//! Converts escape sequences to the corresponding characters +void char_constants< char >::translate_escape_sequences(string_type& str) +{ + using namespace std; // to make sure we can use C functions unqualified + + string_type::iterator it = str.begin(); + while (it != str.end()) + { + it = std::find(it, str.end(), '\\'); + if (std::distance(it, str.end()) >= 2) + { + it = str.erase(it); + switch (*it) + { + case 'n': + *it = '\n'; break; + case 'r': + *it = '\r'; break; + case 'a': + *it = '\a'; break; + case '\\': + ++it; break; + case 't': + *it = '\t'; break; + case 'b': + *it = '\b'; break; + case 'x': + { + string_type::iterator b = it; + if (std::distance(++b, str.end()) >= 2) + { + char_type c1 = *b++, c2 = *b++; + if (isxdigit(c1) && isxdigit(c2)) + { + *it++ = char_type((to_number(c1) << 4) | to_number(c2)); + it = str.erase(it, b); + } + } + break; + } + default: + { + if (*it >= '0' && *it <= '7') + { + string_type::iterator b = it; + int c = (*b++) - '0'; + if (*b >= '0' && *b <= '7') + c = c * 8 + (*b++) - '0'; + if (*b >= '0' && *b <= '7') + c = c * 8 + (*b++) - '0'; + + *it++ = char_type(c); + it = str.erase(it, b); + } + break; + } + } + } + } +} + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +#ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_comment; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_comma; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_dot; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_quote; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_percent; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_exclamation; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_and; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_or; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_equal; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_greater; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_less; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_underline; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_backslash; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_section_bracket_left; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_section_bracket_right; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_paren_bracket_left; +const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_paren_bracket_right; + +#endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + +//! Skips spaces in the beginning of the input +const wchar_t* char_constants< wchar_t >::trim_spaces_left(const char_type* begin, const char_type* end) +{ + using namespace std; + while (begin != end && iswspace(*begin)) + ++begin; + return begin; +} + +//! Skips spaces in the end of the input +const wchar_t* char_constants< wchar_t >::trim_spaces_right(const char_type* begin, const char_type* end) +{ + using namespace std; + while (begin != end && iswspace(*(end - 1))) + --end; + return end; +} + +//! Scans for the attribute name placeholder in the input +const wchar_t* char_constants< wchar_t >::scan_attr_placeholder(const char_type* begin, const char_type* end) +{ + using namespace std; + while (begin != end) + { + char_type c = *begin; + if (!iswalnum(c) && c != char_underline) + break; + ++begin; + } + + return begin; +} + +//! Parses an operand string (possibly quoted) from the input +const wchar_t* char_constants< wchar_t >::parse_operand(const char_type* begin, const char_type* end, string_type& operand) +{ + using namespace std; // to make sure we can use C functions unqualified + + const char_type* p = begin; + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Operand value is empty"); + + char_type c = *p; + if (c == char_quote) + { + // The value is specified as a quoted string + const char_type* start = ++p; + for (; p != end; ++p) + { + c = *p; + if (c == char_quote) + { + break; + } + else if (c == char_backslash) + { + ++p; + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the argument value"); + } + } + if (p == end) + BOOST_LOG_THROW_DESCR(parse_error, "Unterminated quoted string in the argument value"); + + operand.assign(start, p); + translate_escape_sequences(operand); + + ++p; // skip the closing quote + } + else + { + // The value is specified as a single word + const char_type* start = p; + for (++p; p != end; ++p) + { + c = *p; + if (!iswalnum(c) && c != L'_' && c != L'-' && c != L'+' && c != L'.') + break; + } + + operand.assign(start, p); + } + + return p; +} + +//! Converts escape sequences to the corresponding characters +void char_constants< wchar_t >::translate_escape_sequences(string_type& str) +{ + using namespace std; // to make sure we can use C functions unqualified + + string_type::iterator it = str.begin(); + while (it != str.end()) + { + it = std::find(it, str.end(), L'\\'); + if (std::distance(it, str.end()) >= 2) + { + it = str.erase(it); + switch (*it) + { + case L'n': + *it = L'\n'; break; + case L'r': + *it = L'\r'; break; + case L'a': + *it = L'\a'; break; + case L'\\': + ++it; break; + case L't': + *it = L'\t'; break; + case L'b': + *it = L'\b'; break; + case L'x': + { + string_type::iterator b = it; + if (std::distance(++b, str.end()) >= 2) + { + char_type c1 = *b++, c2 = *b++; + if (iswxdigit(c1) && iswxdigit(c2)) + { + *it++ = char_type((to_number(c1) << 4) | to_number(c2)); + it = str.erase(it, b); + } + } + break; + } + case L'u': + { + string_type::iterator b = it; + if (std::distance(++b, str.end()) >= 4) + { + char_type c1 = *b++, c2 = *b++, c3 = *b++, c4 = *b++; + if (iswxdigit(c1) && iswxdigit(c2) && iswxdigit(c3) && iswxdigit(c4)) + { + *it++ = char_type( + (to_number(c1) << 12) | + (to_number(c2) << 8) | + (to_number(c3) << 4) | + to_number(c4)); + it = str.erase(it, b); + } + } + break; + } + case L'U': + { + string_type::iterator b = it; + if (std::distance(++b, str.end()) >= 8) + { + char_type c1 = *b++, c2 = *b++, c3 = *b++, c4 = *b++; + char_type c5 = *b++, c6 = *b++, c7 = *b++, c8 = *b++; + if (iswxdigit(c1) && iswxdigit(c2) && iswxdigit(c3) && iswxdigit(c4) && + iswxdigit(c5) && iswxdigit(c6) && iswxdigit(c7) && iswxdigit(c8)) + { + *it++ = char_type( + (to_number(c1) << 28) | + (to_number(c2) << 24) | + (to_number(c3) << 20) | + (to_number(c4) << 16) | + (to_number(c5) << 12) | + (to_number(c6) << 8) | + (to_number(c7) << 4) | + to_number(c8)); + it = str.erase(it, b); + } + } + break; + } + default: + { + if (*it >= L'0' && *it <= L'7') + { + string_type::iterator b = it; + int c = (*b++) - L'0'; + if (*b >= L'0' && *b <= L'7') + c = c * 8 + (*b++) - L'0'; + if (*b >= L'0' && *b <= L'7') + c = c * 8 + (*b++) - L'0'; + + *it++ = char_type(c); + it = str.erase(it, b); + } + break; + } + } + } + } +} + +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS diff --git a/src/boost/libs/log/src/setup/parser_utils.hpp b/src/boost/libs/log/src/setup/parser_utils.hpp new file mode 100644 index 00000000..bf0be4b8 --- /dev/null +++ b/src/boost/libs/log/src/setup/parser_utils.hpp @@ -0,0 +1,323 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 parser_utils.hpp + * \author Andrey Semashev + * \date 31.03.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_PARSER_UTILS_HPP_INCLUDED_ +#define BOOST_LOG_PARSER_UTILS_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <string> +#include <iostream> +#include <cctype> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/detail/header.hpp> + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Some constants and algorithms needed for parsing +template< typename > struct char_constants; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct char_constants< char > +{ + typedef char char_type; + typedef std::basic_string< char_type > string_type; + typedef boost::log::basic_string_literal< char_type > literal_type; + + static const char_type char_comment = '#'; + static const char_type char_comma = ','; + static const char_type char_dot = '.'; + static const char_type char_quote = '"'; + static const char_type char_percent = '%'; + static const char_type char_exclamation = '!'; + static const char_type char_and = '&'; + static const char_type char_or = '|'; + static const char_type char_equal = '='; + static const char_type char_greater = '>'; + static const char_type char_less = '<'; + static const char_type char_underline = '_'; + static const char_type char_backslash = '\\'; + static const char_type char_section_bracket_left = '['; + static const char_type char_section_bracket_right = ']'; + static const char_type char_paren_bracket_left = '('; + static const char_type char_paren_bracket_right = ')'; + + static const char_type* not_keyword() { return "not"; } + static const char_type* and_keyword() { return "and"; } + static const char_type* or_keyword() { return "or"; } + static const char_type* equal_keyword() { return "="; } + static const char_type* greater_keyword() { return ">"; } + static const char_type* less_keyword() { return "<"; } + static const char_type* not_equal_keyword() { return "!="; } + static const char_type* greater_or_equal_keyword() { return ">="; } + static const char_type* less_or_equal_keyword() { return "<="; } + static const char_type* begins_with_keyword() { return "begins_with"; } + static const char_type* ends_with_keyword() { return "ends_with"; } + static const char_type* contains_keyword() { return "contains"; } + static const char_type* matches_keyword() { return "matches"; } + + static const char_type* message_text_keyword() { return "_"; } + + static literal_type true_keyword() { return literal_type("true"); } + static literal_type false_keyword() { return literal_type("false"); } + + static const char_type* default_level_attribute_name() { return "Severity"; } + + static const char_type* core_section_name() { return "Core"; } + static const char_type* sink_section_name_prefix() { return "Sink:"; } + + static const char_type* core_disable_logging_param_name() { return "DisableLogging"; } + static const char_type* filter_param_name() { return "Filter"; } + + static const char_type* sink_destination_param_name() { return "Destination"; } + static const char_type* file_name_param_name() { return "FileName"; } + static const char_type* rotation_size_param_name() { return "RotationSize"; } + static const char_type* rotation_interval_param_name() { return "RotationInterval"; } + static const char_type* rotation_time_point_param_name() { return "RotationTimePoint"; } + static const char_type* append_param_name() { return "Append"; } + static const char_type* enable_final_rotation_param_name() { return "EnableFinalRotation"; } + static const char_type* auto_flush_param_name() { return "AutoFlush"; } + static const char_type* auto_newline_mode_param_name() { return "AutoNewline"; } + static const char_type* asynchronous_param_name() { return "Asynchronous"; } + static const char_type* format_param_name() { return "Format"; } + static const char_type* provider_id_param_name() { return "ProviderID"; } + static const char_type* log_name_param_name() { return "LogName"; } + static const char_type* source_name_param_name() { return "LogSource"; } + static const char_type* registration_param_name() { return "Registration"; } + static const char_type* local_address_param_name() { return "LocalAddress"; } + static const char_type* target_address_param_name() { return "TargetAddress"; } + static const char_type* target_param_name() { return "Target"; } + static const char_type* max_size_param_name() { return "MaxSize"; } + static const char_type* max_files_param_name() { return "MaxFiles"; } + static const char_type* min_free_space_param_name() { return "MinFreeSpace"; } + static const char_type* scan_for_files_param_name() { return "ScanForFiles"; } + + static const char_type* scan_method_all() { return "All"; } + static const char_type* scan_method_matching() { return "Matching"; } + + static const char_type* auto_newline_mode_disabled() { return "Disabled"; } + static const char_type* auto_newline_mode_always_insert() { return "AlwaysInsert"; } + static const char_type* auto_newline_mode_insert_if_missing() { return "InsertIfMissing"; } + + static const char_type* registration_never() { return "Never"; } + static const char_type* registration_on_demand() { return "OnDemand"; } + static const char_type* registration_forced() { return "Forced"; } + + static const char_type* text_file_destination() { return "TextFile"; } + static const char_type* console_destination() { return "Console"; } + static const char_type* syslog_destination() { return "Syslog"; } + static const char_type* simple_event_log_destination() { return "SimpleEventLog"; } + static const char_type* debugger_destination() { return "Debugger"; } + + static literal_type monday_keyword() { return literal_type("Monday"); } + static literal_type short_monday_keyword() { return literal_type("Mon"); } + static literal_type tuesday_keyword() { return literal_type("Tuesday"); } + static literal_type short_tuesday_keyword() { return literal_type("Tue"); } + static literal_type wednesday_keyword() { return literal_type("Wednesday"); } + static literal_type short_wednesday_keyword() { return literal_type("Wed"); } + static literal_type thursday_keyword() { return literal_type("Thursday"); } + static literal_type short_thursday_keyword() { return literal_type("Thu"); } + static literal_type friday_keyword() { return literal_type("Friday"); } + static literal_type short_friday_keyword() { return literal_type("Fri"); } + static literal_type saturday_keyword() { return literal_type("Saturday"); } + static literal_type short_saturday_keyword() { return literal_type("Sat"); } + static literal_type sunday_keyword() { return literal_type("Sunday"); } + static literal_type short_sunday_keyword() { return literal_type("Sun"); } + + static std::ostream& get_console_log_stream() { return std::clog; } + + static int to_number(char_type c) + { + using namespace std; // to make sure we can use C functions unqualified + int n = 0; + if (isdigit(c)) + n = c - '0'; + else if (c >= 'a' && c <= 'f') + n = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + n = c - 'A' + 10; + return n; + } + + //! Skips spaces in the beginning of the input + static const char_type* trim_spaces_left(const char_type* begin, const char_type* end); + //! Skips spaces in the end of the input + static const char_type* trim_spaces_right(const char_type* begin, const char_type* end); + //! Scans for the attribute name placeholder in the input + static const char_type* scan_attr_placeholder(const char_type* begin, const char_type* end); + //! Parses an operand string (possibly quoted) from the input + static const char_type* parse_operand(const char_type* begin, const char_type* end, string_type& operand); + //! Converts escape sequences to the corresponding characters + static void translate_escape_sequences(string_type& str); +}; +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct char_constants< wchar_t > +{ + typedef wchar_t char_type; + typedef std::basic_string< char_type > string_type; + typedef boost::log::basic_string_literal< char_type > literal_type; + + static const char_type char_comment = L'#'; + static const char_type char_comma = L','; + static const char_type char_dot = L'.'; + static const char_type char_quote = L'"'; + static const char_type char_percent = L'%'; + static const char_type char_exclamation = L'!'; + static const char_type char_and = L'&'; + static const char_type char_or = L'|'; + static const char_type char_equal = L'='; + static const char_type char_greater = L'>'; + static const char_type char_less = L'<'; + static const char_type char_underline = L'_'; + static const char_type char_backslash = L'\\'; + static const char_type char_section_bracket_left = L'['; + static const char_type char_section_bracket_right = L']'; + static const char_type char_paren_bracket_left = L'('; + static const char_type char_paren_bracket_right = L')'; + + static const char_type* not_keyword() { return L"not"; } + static const char_type* and_keyword() { return L"and"; } + static const char_type* or_keyword() { return L"or"; } + static const char_type* equal_keyword() { return L"="; } + static const char_type* greater_keyword() { return L">"; } + static const char_type* less_keyword() { return L"<"; } + static const char_type* not_equal_keyword() { return L"!="; } + static const char_type* greater_or_equal_keyword() { return L">="; } + static const char_type* less_or_equal_keyword() { return L"<="; } + static const char_type* begins_with_keyword() { return L"begins_with"; } + static const char_type* ends_with_keyword() { return L"ends_with"; } + static const char_type* contains_keyword() { return L"contains"; } + static const char_type* matches_keyword() { return L"matches"; } + + static const char_type* message_text_keyword() { return L"_"; } + + static literal_type true_keyword() { return literal_type(L"true"); } + static literal_type false_keyword() { return literal_type(L"false"); } + + static const char_type* default_level_attribute_name() { return L"Severity"; } + + static const char_type* core_section_name() { return L"Core"; } + static const char_type* sink_section_name_prefix() { return L"Sink:"; } + + static const char_type* core_disable_logging_param_name() { return L"DisableLogging"; } + static const char_type* filter_param_name() { return L"Filter"; } + + static const char_type* sink_destination_param_name() { return L"Destination"; } + static const char_type* file_name_param_name() { return L"FileName"; } + static const char_type* rotation_size_param_name() { return L"RotationSize"; } + static const char_type* rotation_interval_param_name() { return L"RotationInterval"; } + static const char_type* rotation_time_point_param_name() { return L"RotationTimePoint"; } + static const char_type* append_param_name() { return L"Append"; } + static const char_type* enable_final_rotation_param_name() { return L"EnableFinalRotation"; } + static const char_type* auto_flush_param_name() { return L"AutoFlush"; } + static const char_type* auto_newline_mode_param_name() { return L"AutoNewline"; } + static const char_type* asynchronous_param_name() { return L"Asynchronous"; } + static const char_type* format_param_name() { return L"Format"; } + static const char_type* provider_id_param_name() { return L"ProviderID"; } + static const char_type* log_name_param_name() { return L"LogName"; } + static const char_type* source_name_param_name() { return L"LogSource"; } + static const char_type* registration_param_name() { return L"Registration"; } + static const char_type* local_address_param_name() { return L"LocalAddress"; } + static const char_type* target_address_param_name() { return L"TargetAddress"; } + static const char_type* target_param_name() { return L"Target"; } + static const char_type* max_size_param_name() { return L"MaxSize"; } + static const char_type* max_files_param_name() { return L"MaxFiles"; } + static const char_type* min_free_space_param_name() { return L"MinFreeSpace"; } + static const char_type* scan_for_files_param_name() { return L"ScanForFiles"; } + + static const char_type* scan_method_all() { return L"All"; } + static const char_type* scan_method_matching() { return L"Matching"; } + + static const char_type* auto_newline_mode_disabled() { return L"Disabled"; } + static const char_type* auto_newline_mode_always_insert() { return L"AlwaysInsert"; } + static const char_type* auto_newline_mode_insert_if_missing() { return L"InsertIfMissing"; } + + static const char_type* registration_never() { return L"Never"; } + static const char_type* registration_on_demand() { return L"OnDemand"; } + static const char_type* registration_forced() { return L"Forced"; } + + static const char_type* text_file_destination() { return L"TextFile"; } + static const char_type* console_destination() { return L"Console"; } + static const char_type* syslog_destination() { return L"Syslog"; } + static const char_type* simple_event_log_destination() { return L"SimpleEventLog"; } + static const char_type* debugger_destination() { return L"Debugger"; } + + static literal_type monday_keyword() { return literal_type(L"Monday"); } + static literal_type short_monday_keyword() { return literal_type(L"Mon"); } + static literal_type tuesday_keyword() { return literal_type(L"Tuesday"); } + static literal_type short_tuesday_keyword() { return literal_type(L"Tue"); } + static literal_type wednesday_keyword() { return literal_type(L"Wednesday"); } + static literal_type short_wednesday_keyword() { return literal_type(L"Wed"); } + static literal_type thursday_keyword() { return literal_type(L"Thursday"); } + static literal_type short_thursday_keyword() { return literal_type(L"Thu"); } + static literal_type friday_keyword() { return literal_type(L"Friday"); } + static literal_type short_friday_keyword() { return literal_type(L"Fri"); } + static literal_type saturday_keyword() { return literal_type(L"Saturday"); } + static literal_type short_saturday_keyword() { return literal_type(L"Sat"); } + static literal_type sunday_keyword() { return literal_type(L"Sunday"); } + static literal_type short_sunday_keyword() { return literal_type(L"Sun"); } + + static std::wostream& get_console_log_stream() { return std::wclog; } + + static int to_number(char_type c) + { + int n = 0; + if (c >= L'0' && c <= L'9') + n = c - L'0'; + else if (c >= L'a' && c <= L'f') + n = c - L'a' + 10; + else if (c >= L'A' && c <= L'F') + n = c - L'A' + 10; + return n; + } + + static bool iswxdigit(char_type c) + { + return (c >= L'0' && c <= L'9') || (c >= L'a' && c <= L'f') || (c >= L'A' && c <= L'F'); + } + + //! Skips spaces in the beginning of the input + static const char_type* trim_spaces_left(const char_type* begin, const char_type* end); + //! Skips spaces in the end of the input + static const char_type* trim_spaces_right(const char_type* begin, const char_type* end); + //! Scans for the attribute name placeholder in the input + static const char_type* scan_attr_placeholder(const char_type* begin, const char_type* end); + //! Parses an operand string (possibly quoted) from the input + static const char_type* parse_operand(const char_type* begin, const char_type* end, string_type& operand); + //! Converts escape sequences to the corresponding characters + static void translate_escape_sequences(string_type& str); +}; +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_PARSER_UTILS_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/setup/settings_parser.cpp b/src/boost/libs/log/src/setup/settings_parser.cpp new file mode 100644 index 00000000..d0ebe6fe --- /dev/null +++ b/src/boost/libs/log/src/setup/settings_parser.cpp @@ -0,0 +1,260 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 settings_parser.cpp + * \author Andrey Semashev + * \date 20.07.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS + +#include <boost/log/detail/setup_config.hpp> +#include <string> +#include <locale> +#include <iostream> +#include <stdexcept> +#include <algorithm> +#include <boost/throw_exception.hpp> +#include <boost/exception/exception.hpp> +#include <boost/exception/info.hpp> +#include <boost/exception/errinfo_at_line.hpp> +#include <boost/io/ios_state.hpp> +#include <boost/move/core.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/utility/setup/settings_parser.hpp> +#include <boost/log/exceptions.hpp> +#include "parser_utils.hpp" +#include "spirit_encoding.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! Settings parser +template< typename CharT > +class settings_parser +{ +private: + typedef CharT char_type; + typedef const char_type* iterator_type; + typedef typename log::aux::encoding< char_type >::type encoding; + typedef settings_parser< char_type > this_type; + + typedef std::basic_string< char_type > string_type; + typedef log::aux::char_constants< char_type > constants; + typedef basic_settings< char_type > settings_type; + +private: + //! Current section name + std::string m_SectionName; + //! Current parameter name + std::string m_ParameterName; + //! Settings instance + settings_type& m_Settings; + //! Locale from the source stream + std::locale m_Locale; + //! Current line number + unsigned int& m_LineCounter; + +public: + //! Constructor + explicit settings_parser(settings_type& setts, unsigned int& line_counter, std::locale const& loc) : + m_Settings(setts), + m_Locale(loc), + m_LineCounter(line_counter) + { + } + + //! Parses a line of the input + void parse_line(iterator_type& begin, iterator_type end) + { + iterator_type p = begin; + p = constants::trim_spaces_left(p, end); + if (p != end) + { + char_type c = *p; + if (c == constants::char_section_bracket_left) + { + // We have a section name + iterator_type start = ++p; + start = constants::trim_spaces_left(start, end); + iterator_type stop = std::find(start, end, constants::char_section_bracket_right); + if (stop == end) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section header is invalid", (m_LineCounter)); + + p = stop + 1; + stop = constants::trim_spaces_right(start, stop); + + set_section_name(start, stop); + } + else if (c != constants::char_comment) + { + // We have a parameter + iterator_type eq = std::find(p, end, constants::char_equal); + if (eq == end) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter description is invalid", (m_LineCounter)); + + // Parameter name + set_parameter_name(p, constants::trim_spaces_right(p, eq)); + + // Parameter value + p = constants::trim_spaces_left(eq + 1, end); + if (p == end || *p == constants::char_comment) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter value is not specified", (m_LineCounter)); + + try + { + string_type value; + p = constants::parse_operand(p, end, value); + set_parameter_value(value); + } + catch (parse_error& e) + { + throw boost::enable_error_info(e) << boost::errinfo_at_line(m_LineCounter); + } + } + + // In the end of the line we may have a comment + p = constants::trim_spaces_left(p, end); + if (p != end) + { + c = *p; + if (c == constants::char_comment) + { + // The comment spans until the end of the line + p = end; + } + else + { + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Unexpected characters in the end of the line", (m_LineCounter)); + } + } + } + + begin = p; + } + +private: + //! The method sets the parsed section name + void set_section_name(iterator_type begin, iterator_type end) + { + // Check that the section name is valid + if (begin == end) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section name is empty", (m_LineCounter)); + + for (iterator_type p = begin; p != end; ++p) + { + char_type c = *p; + if (c != constants::char_dot && !encoding::isalnum(c)) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section name is invalid", (m_LineCounter)); + } + + m_SectionName = log::aux::to_narrow(string_type(begin, end), m_Locale); + + // For compatibility with Boost.Log v1, we replace the "Sink:" prefix with "Sinks." + // so that all sink parameters are placed in the common Sinks section. + if (m_SectionName.compare(0, 5, "Sink:") == 0) + m_SectionName = "Sinks." + m_SectionName.substr(5); + } + + //! The method sets the parsed parameter name + void set_parameter_name(iterator_type begin, iterator_type end) + { + if (m_SectionName.empty()) + { + // The parameter encountered before any section starter + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameters are only allowed within sections", (m_LineCounter)); + } + + // Check that the parameter name is valid + if (begin == end) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is empty", (m_LineCounter)); + + iterator_type p = begin; + if (!encoding::isalpha(*p)) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is invalid", (m_LineCounter)); + for (++p; p != end; ++p) + { + char_type c = *p; + if (!encoding::isgraph(c)) + BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is invalid", (m_LineCounter)); + } + + m_ParameterName = log::aux::to_narrow(string_type(begin, end), m_Locale); + } + + //! The method sets the parsed parameter value (non-quoted) + void set_parameter_value(string_type const& value) + { + m_Settings[m_SectionName][m_ParameterName] = value; + m_ParameterName.clear(); + } + + // Assignment and copying are prohibited + BOOST_DELETED_FUNCTION(settings_parser(settings_parser const&)) + BOOST_DELETED_FUNCTION(settings_parser& operator= (settings_parser const&)) +}; + +} // namespace + +//! The function parses library settings from an input stream +template< typename CharT > +BOOST_LOG_SETUP_API basic_settings< CharT > parse_settings(std::basic_istream< CharT >& strm) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef settings_parser< char_type > settings_parser_type; + typedef basic_settings< char_type > settings_type; + + if (!strm.good()) + BOOST_THROW_EXCEPTION(std::invalid_argument("The input stream for parsing settings is not valid")); + + io::basic_ios_exception_saver< char_type > exceptions_guard(strm, std::ios_base::badbit); + + // Engage parsing + settings_type settings; + unsigned int line_number = 1; + std::locale loc = strm.getloc(); + settings_parser_type parser(settings, line_number, loc); + + string_type line; + while (!strm.eof()) + { + std::getline(strm, line); + + const char_type* p = line.c_str(); + parser.parse_line(p, p + line.size()); + + line.clear(); + ++line_number; + } + + return BOOST_LOG_NRVO_RESULT(settings); +} + + +#ifdef BOOST_LOG_USE_CHAR +template BOOST_LOG_SETUP_API basic_settings< char > parse_settings< char >(std::basic_istream< char >& strm); +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_SETUP_API basic_settings< wchar_t > parse_settings< wchar_t >(std::basic_istream< wchar_t >& strm); +#endif + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS diff --git a/src/boost/libs/log/src/severity_level.cpp b/src/boost/libs/log/src/severity_level.cpp new file mode 100644 index 00000000..25b7c7d5 --- /dev/null +++ b/src/boost/libs/log/src/severity_level.cpp @@ -0,0 +1,92 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 severity_level.cpp + * \author Andrey Semashev + * \date 10.05.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/log/sources/severity_feature.hpp> + +#if !defined(BOOST_LOG_NO_THREADS) && !defined(BOOST_LOG_USE_COMPILER_TLS) +#include <boost/bind.hpp> +#include <boost/checked_delete.hpp> +#include <boost/thread/thread.hpp> // at_thread_exit +#include <boost/log/detail/singleton.hpp> +#include <boost/log/detail/thread_specific.hpp> +#endif +#include "unique_ptr.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sources { + +namespace aux { + +#if defined(BOOST_LOG_NO_THREADS) + +static uintmax_t g_Severity = 0; + +#elif defined(BOOST_LOG_USE_COMPILER_TLS) + +static BOOST_LOG_TLS uintmax_t g_Severity = 0; + +#else + +//! Severity level storage class +class severity_level_holder : + public boost::log::aux::lazy_singleton< severity_level_holder, boost::log::aux::thread_specific< uintmax_t* > > +{ +}; + +#endif + + +#if !defined(BOOST_LOG_NO_THREADS) && !defined(BOOST_LOG_USE_COMPILER_TLS) + +//! The method returns the severity level for the current thread +BOOST_LOG_API uintmax_t& get_severity_level() +{ + boost::log::aux::thread_specific< uintmax_t* >& tss = severity_level_holder::get(); + uintmax_t* p = tss.get(); + if (BOOST_UNLIKELY(!p)) + { + log::aux::unique_ptr< uintmax_t > ptr(new uintmax_t(0)); + tss.set(ptr.get()); + p = ptr.release(); + boost::this_thread::at_thread_exit(boost::bind(checked_deleter< uintmax_t >(), p)); + } + return *p; +} + +#else // !defined(BOOST_LOG_NO_THREADS) && !defined(BOOST_LOG_USE_COMPILER_TLS) + +//! The method returns the severity level for the current thread +BOOST_LOG_API uintmax_t& get_severity_level() +{ + return g_Severity; +} + +#endif // !defined(BOOST_LOG_NO_THREADS) && !defined(BOOST_LOG_USE_COMPILER_TLS) + +} // namespace aux + +} // namespace sources + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/spirit_encoding.cpp b/src/boost/libs/log/src/spirit_encoding.cpp new file mode 100644 index 00000000..1222435e --- /dev/null +++ b/src/boost/libs/log/src/spirit_encoding.cpp @@ -0,0 +1,48 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 spirit_encoding.cpp + * \author Andrey Semashev + * \date 20.07.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/preprocessor/tuple/elem.hpp> +#include <boost/preprocessor/seq/for_each.hpp> +#include "spirit_encoding.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#define BOOST_LOG_DEFINE_CHARSET_PARSER(r, charset, parser)\ + BOOST_LOG_API encoding_specific< spirit::char_encoding::charset >::BOOST_PP_TUPLE_ELEM(2, 0, parser) const&\ + encoding_specific< spirit::char_encoding::charset >::BOOST_PP_TUPLE_ELEM(2, 1, parser) =\ + spirit::charset::BOOST_PP_TUPLE_ELEM(2, 1, parser); + +#define BOOST_LOG_DEFINE_CHARSET_PARSERS(charset)\ + BOOST_PP_SEQ_FOR_EACH(BOOST_LOG_DEFINE_CHARSET_PARSER, charset, BOOST_LOG_CHARSET_PARSERS) + +BOOST_LOG_DEFINE_CHARSET_PARSERS(standard) +BOOST_LOG_DEFINE_CHARSET_PARSERS(standard_wide) + +#undef BOOST_LOG_DEFINE_CHARSET_PARSERS +#undef BOOST_LOG_DEFINE_CHARSET_PARSER + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/spirit_encoding.hpp b/src/boost/libs/log/src/spirit_encoding.hpp new file mode 100644 index 00000000..65429078 --- /dev/null +++ b/src/boost/libs/log/src/spirit_encoding.hpp @@ -0,0 +1,104 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 spirit_encoding.hpp + * \author Andrey Semashev + * \date 20.07.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_SPIRIT_ENCODING_HPP_INCLUDED_ +#define BOOST_LOG_SPIRIT_ENCODING_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/preprocessor/tuple/elem.hpp> +#include <boost/preprocessor/seq/for_each.hpp> +#include <boost/spirit/include/support_standard.hpp> +#include <boost/spirit/include/support_standard_wide.hpp> +#include <boost/spirit/home/support/common_terminals.hpp> +#include <boost/log/detail/header.hpp> + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +template< typename > +struct encoding; + +template< > +struct encoding< char > +{ + typedef spirit::char_encoding::standard type; +}; +template< > +struct encoding< wchar_t > +{ + typedef spirit::char_encoding::standard_wide type; +}; + +//! A simple trait that allows to use charset-specific Qi parsers in a generic way +template< typename EncodingT > +struct encoding_specific; + +#define BOOST_LOG_CHARSET_PARSERS\ + ((char_type, char_))\ + ((string_type, string))\ + ((alnum_type, alnum))\ + ((alpha_type, alpha))\ + ((blank_type, blank))\ + ((cntrl_type, cntrl))\ + ((digit_type, digit))\ + ((graph_type, graph))\ + ((print_type, print))\ + ((punct_type, punct))\ + ((space_type, space))\ + ((xdigit_type, xdigit))\ + ((no_case_type, no_case))\ + ((lower_type, lower))\ + ((upper_type, upper))\ + ((lowernum_type, lowernum))\ + ((uppernum_type, uppernum)) + +#define BOOST_LOG_DECLARE_CHARSET_PARSER(r, charset, parser)\ + typedef spirit::charset::BOOST_PP_TUPLE_ELEM(2, 0, parser) BOOST_PP_TUPLE_ELEM(2, 0, parser);\ + BOOST_LOG_API static BOOST_PP_TUPLE_ELEM(2, 0, parser) const& BOOST_PP_TUPLE_ELEM(2, 1, parser); + +#define BOOST_LOG_DECLARE_CHARSET_PARSERS(charset)\ + BOOST_PP_SEQ_FOR_EACH(BOOST_LOG_DECLARE_CHARSET_PARSER, charset, BOOST_LOG_CHARSET_PARSERS) + +template< > +struct encoding_specific< spirit::char_encoding::standard > +{ + BOOST_LOG_DECLARE_CHARSET_PARSERS(standard) +}; + +template< > +struct encoding_specific< spirit::char_encoding::standard_wide > +{ + BOOST_LOG_DECLARE_CHARSET_PARSERS(standard_wide) +}; + +#undef BOOST_LOG_DECLARE_CHARSET_PARSERS +#undef BOOST_LOG_DECLARE_CHARSET_PARSER + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_SPIRIT_ENCODING_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/stateless_allocator.hpp b/src/boost/libs/log/src/stateless_allocator.hpp new file mode 100644 index 00000000..3377f083 --- /dev/null +++ b/src/boost/libs/log/src/stateless_allocator.hpp @@ -0,0 +1,95 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 stateless_allocator.hpp + * \author Andrey Semashev + * \date 11.02.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_STATELESS_ALLOCATOR_HPP_INCLUDED_ +#define BOOST_LOG_STATELESS_ALLOCATOR_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <cstdlib> +#include <memory> +#include <boost/log/detail/header.hpp> + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#if defined(_STLPORT_VERSION) + +#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) + +template< typename T > +using stateless_allocator = std::allocator< T >; + +#else + +template< typename T > +struct stateless_allocator : + public std::allocator< T > +{ +}; + +#endif + +#else + +template< typename T > +struct stateless_allocator +{ + template< typename U > + struct rebind + { + typedef stateless_allocator< U > other; + }; + + typedef T value_type; + typedef value_type* pointer; + typedef value_type const* const_pointer; + typedef value_type& reference; + typedef value_type const& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static pointer allocate(size_type n, const void* = NULL) + { + pointer p = static_cast< pointer >(std::malloc(n * sizeof(value_type))); + if (p) + return p; + else + throw std::bad_alloc(); + } + static void deallocate(pointer p, size_type) + { + std::free(p); + } +}; + +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_STATELESS_ALLOCATOR_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/syslog_backend.cpp b/src/boost/libs/log/src/syslog_backend.cpp new file mode 100644 index 00000000..1c359454 --- /dev/null +++ b/src/boost/libs/log/src/syslog_backend.cpp @@ -0,0 +1,602 @@ +/* + * Copyright Andrey Semashev 2007 - 2018. + * 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 syslog_backend.cpp + * \author Andrey Semashev + * \date 08.01.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WITHOUT_SYSLOG + +#include <boost/log/detail/config.hpp> +#include <ctime> +#include <algorithm> +#include <stdexcept> +#include <boost/limits.hpp> +#include <boost/assert.hpp> +#include <boost/smart_ptr/weak_ptr.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/throw_exception.hpp> +#if !defined(BOOST_LOG_NO_ASIO) +#include <boost/asio/buffer.hpp> +#include <boost/asio/socket_base.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/udp.hpp> +#include <boost/asio/ip/address.hpp> +#include <boost/asio/ip/host_name.hpp> +#include <boost/asio/ip/resolver_base.hpp> +#endif +#include <boost/system/error_code.hpp> +#include <boost/date_time/c_time.hpp> +#include <boost/log/sinks/syslog_backend.hpp> +#include <boost/log/sinks/syslog_constants.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/detail/snprintf.hpp> +#include <boost/log/exceptions.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/locks.hpp> +#include <boost/thread/mutex.hpp> +#endif +#include "unique_ptr.hpp" + +#ifdef BOOST_LOG_USE_NATIVE_SYSLOG +#include <syslog.h> +#endif // BOOST_LOG_USE_NATIVE_SYSLOG + +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +namespace syslog { + + //! The function constructs log record level from an integer + BOOST_LOG_API level make_level(int lev) + { + if (BOOST_UNLIKELY(static_cast< unsigned int >(lev) >= 8u)) + BOOST_THROW_EXCEPTION(std::out_of_range("syslog level value is out of range")); + return static_cast< level >(lev); + } + + //! The function constructs log source facility from an integer + BOOST_LOG_API facility make_facility(int fac) + { + if (BOOST_UNLIKELY((static_cast< unsigned int >(fac) & 7u) != 0u + || static_cast< unsigned int >(fac) > (23u * 8u))) + { + BOOST_THROW_EXCEPTION(std::out_of_range("syslog facility code value is out of range")); + } + return static_cast< facility >(fac); + } + +} // namespace syslog + +//////////////////////////////////////////////////////////////////////////////// +//! Syslog sink backend implementation +//////////////////////////////////////////////////////////////////////////////// +struct syslog_backend::implementation +{ +#ifdef BOOST_LOG_USE_NATIVE_SYSLOG + struct native; +#endif // BOOST_LOG_USE_NATIVE_SYSLOG +#if !defined(BOOST_LOG_NO_ASIO) + struct udp_socket_based; +#endif + + //! Level mapper + severity_mapper_type m_LevelMapper; + + //! Logging facility (portable or native, depending on the backend implementation) + const int m_Facility; + + //! Constructor + explicit implementation(int facility) : + m_Facility(facility) + { + } + //! Virtual destructor + virtual ~implementation() {} + + //! The method sends the formatted message to the syslog host + virtual void send(syslog::level lev, string_type const& formatted_message) = 0; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Native syslog API support +//////////////////////////////////////////////////////////////////////////////// + +#ifdef BOOST_LOG_USE_NATIVE_SYSLOG + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + //! Syslog service initializer (implemented as a weak singleton) +#if !defined(BOOST_LOG_NO_THREADS) + class native_syslog_initializer : + private log::aux::lazy_singleton< native_syslog_initializer, mutex > +#else + class native_syslog_initializer +#endif + { +#if !defined(BOOST_LOG_NO_THREADS) + friend class log::aux::lazy_singleton< native_syslog_initializer, mutex >; + typedef log::aux::lazy_singleton< native_syslog_initializer, mutex > mutex_holder; +#endif + + public: + native_syslog_initializer(std::string const& ident, int facility) + { + ::openlog((ident.empty() ? static_cast< const char* >(NULL) : ident.c_str()), 0, facility); + } + ~native_syslog_initializer() + { + ::closelog(); + } + + static shared_ptr< native_syslog_initializer > get_instance(std::string const& ident, int facility) + { +#if !defined(BOOST_LOG_NO_THREADS) + lock_guard< mutex > lock(mutex_holder::get()); +#endif + static weak_ptr< native_syslog_initializer > instance; + shared_ptr< native_syslog_initializer > p(instance.lock()); + if (!p) + { + p = boost::make_shared< native_syslog_initializer >(ident, facility); + instance = p; + } + return p; + } + }; + +} // namespace + +struct syslog_backend::implementation::native : + public implementation +{ + //! Reference to the syslog service initializer + const shared_ptr< native_syslog_initializer > m_pSyslogInitializer; + + //! Constructor + native(syslog::facility const& fac, std::string const& ident) : + implementation(convert_facility(fac)), + m_pSyslogInitializer(native_syslog_initializer::get_instance(ident, this->m_Facility)) + { + } + + //! The method sends the formatted message to the syslog host + void send(syslog::level lev, string_type const& formatted_message) + { + int native_level; + switch (lev) + { + case syslog::emergency: + native_level = LOG_EMERG; break; + case syslog::alert: + native_level = LOG_ALERT; break; + case syslog::critical: + native_level = LOG_CRIT; break; + case syslog::error: + native_level = LOG_ERR; break; + case syslog::warning: + native_level = LOG_WARNING; break; + case syslog::notice: + native_level = LOG_NOTICE; break; + case syslog::debug: + native_level = LOG_DEBUG; break; + default: + native_level = LOG_INFO; break; + } + + ::syslog(this->m_Facility | native_level, "%s", formatted_message.c_str()); + } + +private: + //! The function converts portable facility codes to the native codes + static int convert_facility(syslog::facility const& fac) + { + // POSIX does not specify anything except for LOG_USER and LOG_LOCAL* + #ifndef LOG_KERN + #define LOG_KERN LOG_USER + #endif + #ifndef LOG_DAEMON + #define LOG_DAEMON LOG_KERN + #endif + #ifndef LOG_MAIL + #define LOG_MAIL LOG_USER + #endif + #ifndef LOG_AUTH + #define LOG_AUTH LOG_DAEMON + #endif + #ifndef LOG_SYSLOG + #define LOG_SYSLOG LOG_DAEMON + #endif + #ifndef LOG_LPR + #define LOG_LPR LOG_DAEMON + #endif + #ifndef LOG_NEWS + #define LOG_NEWS LOG_USER + #endif + #ifndef LOG_UUCP + #define LOG_UUCP LOG_USER + #endif + #ifndef LOG_CRON + #define LOG_CRON LOG_DAEMON + #endif + #ifndef LOG_AUTHPRIV + #define LOG_AUTHPRIV LOG_AUTH + #endif + #ifndef LOG_FTP + #define LOG_FTP LOG_DAEMON + #endif + + static const int native_facilities[24] = + { + LOG_KERN, + LOG_USER, + LOG_MAIL, + LOG_DAEMON, + LOG_AUTH, + LOG_SYSLOG, + LOG_LPR, + LOG_NEWS, + LOG_UUCP, + LOG_CRON, + LOG_AUTHPRIV, + LOG_FTP, + + // reserved values + LOG_USER, + LOG_USER, + LOG_USER, + LOG_USER, + + LOG_LOCAL0, + LOG_LOCAL1, + LOG_LOCAL2, + LOG_LOCAL3, + LOG_LOCAL4, + LOG_LOCAL5, + LOG_LOCAL6, + LOG_LOCAL7 + }; + + std::size_t n = static_cast< unsigned int >(fac) / 8u; + BOOST_ASSERT(n < sizeof(native_facilities) / sizeof(*native_facilities)); + return native_facilities[n]; + } +}; + +#endif // BOOST_LOG_USE_NATIVE_SYSLOG + + +//////////////////////////////////////////////////////////////////////////////// +// Socket-based implementation +//////////////////////////////////////////////////////////////////////////////// + +#if !defined(BOOST_LOG_NO_ASIO) + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + //! The shared UDP socket + struct syslog_udp_socket + { + private: + //! The socket primitive + asio::ip::udp::socket m_Socket; + + public: + //! The constructor creates a socket bound to the specified local address and port + explicit syslog_udp_socket(asio::io_context& io_ctx, asio::ip::udp const& protocol, asio::ip::udp::endpoint const& local_address) : + m_Socket(io_ctx) + { + m_Socket.open(protocol); + m_Socket.set_option(asio::socket_base::reuse_address(true)); + m_Socket.bind(local_address); + } + //! The destructor closes the socket + ~syslog_udp_socket() + { + boost::system::error_code ec; + m_Socket.shutdown(asio::socket_base::shutdown_both, ec); + m_Socket.close(ec); + } + + //! The method sends the syslog message to the specified endpoint + void send_message(int pri, const char* local_host_name, asio::ip::udp::endpoint const& target, const char* message); + + BOOST_DELETED_FUNCTION(syslog_udp_socket(syslog_udp_socket const&)) + BOOST_DELETED_FUNCTION(syslog_udp_socket& operator= (syslog_udp_socket const&)) + }; + + //! The class contains the UDP service for syslog sockets to function + class syslog_udp_service : + public log::aux::lazy_singleton< syslog_udp_service, shared_ptr< syslog_udp_service > > + { + friend class log::aux::lazy_singleton< syslog_udp_service, shared_ptr< syslog_udp_service > >; + typedef log::aux::lazy_singleton< syslog_udp_service, shared_ptr< syslog_udp_service > > base_type; + + public: + //! The IO context instance + asio::io_context m_IOContext; + //! The local host name to put into log message + std::string m_LocalHostName; + +#if !defined(BOOST_LOG_NO_THREADS) + //! A synchronization primitive to protect the host name resolver + mutex m_Mutex; + //! The resolver is used to acquire connection endpoints + asio::ip::udp::resolver m_HostNameResolver; +#endif // !defined(BOOST_LOG_NO_THREADS) + + private: + //! Default constructor + syslog_udp_service() +#if !defined(BOOST_LOG_NO_THREADS) + : m_HostNameResolver(m_IOContext) +#endif // !defined(BOOST_LOG_NO_THREADS) + { + boost::system::error_code err; + m_LocalHostName = asio::ip::host_name(err); + } + //! Initializes the singleton instance + static void init_instance() + { + base_type::get_instance().reset(new syslog_udp_service()); + } + }; + + //! The method sends the syslog message to the specified endpoint + void syslog_udp_socket::send_message( + int pri, const char* local_host_name, asio::ip::udp::endpoint const& target, const char* message) + { + std::time_t t = std::time(NULL); + std::tm ts; + std::tm* time_stamp = boost::date_time::c_time::localtime(&t, &ts); + + // Month will have to be injected separately, as involving locale won't do here + static const char months[12][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + // The packet size is mandated in RFC3164, plus one for the terminating zero + char packet[1025]; + int n = boost::log::aux::snprintf + ( + packet, + sizeof(packet), + "<%d>%s %2d %02d:%02d:%02d %s %s", + pri, + months[time_stamp->tm_mon], + time_stamp->tm_mday, + time_stamp->tm_hour, + time_stamp->tm_min, + time_stamp->tm_sec, + local_host_name, + message + ); + if (BOOST_LIKELY(n > 0)) + { + std::size_t packet_size = static_cast< std::size_t >(n) >= sizeof(packet) ? sizeof(packet) - 1u : static_cast< std::size_t >(n); + m_Socket.send_to(asio::buffer(packet, packet_size), target); + } + } + +} // namespace + +struct syslog_backend::implementation::udp_socket_based : + public implementation +{ + //! Protocol to be used + asio::ip::udp m_Protocol; + //! Pointer to the list of sockets + shared_ptr< syslog_udp_service > m_pService; + //! Pointer to the socket being used + log::aux::unique_ptr< syslog_udp_socket > m_pSocket; + //! The target host to send packets to + asio::ip::udp::endpoint m_TargetHost; + + //! Constructor + explicit udp_socket_based(syslog::facility const& fac, asio::ip::udp const& protocol) : + implementation(fac), + m_Protocol(protocol), + m_pService(syslog_udp_service::get()) + { + if (m_Protocol == asio::ip::udp::v4()) + { + m_TargetHost = asio::ip::udp::endpoint(asio::ip::address_v4(0x7F000001), 514); // 127.0.0.1:514 + } + else + { + // ::1, port 514 + asio::ip::address_v6::bytes_type addr; + std::fill_n(addr.data(), addr.size() - 1u, static_cast< unsigned char >(0u)); + addr[addr.size() - 1u] = 1u; + m_TargetHost = asio::ip::udp::endpoint(asio::ip::address_v6(addr), 514); + } + } + + //! The method sends the formatted message to the syslog host + void send(syslog::level lev, string_type const& formatted_message) + { + if (!m_pSocket.get()) + { + asio::ip::udp::endpoint any_local_address; + m_pSocket.reset(new syslog_udp_socket(m_pService->m_IOContext, m_Protocol, any_local_address)); + } + + m_pSocket->send_message( + this->m_Facility | static_cast< int >(lev), + m_pService->m_LocalHostName.c_str(), + m_TargetHost, + formatted_message.c_str()); + } +}; + +#endif // !defined(BOOST_LOG_NO_ASIO) + +//////////////////////////////////////////////////////////////////////////////// +// Sink backend implementation +//////////////////////////////////////////////////////////////////////////////// +BOOST_LOG_API syslog_backend::syslog_backend() +{ + construct(log::aux::empty_arg_list()); +} + +//! Destructor +BOOST_LOG_API syslog_backend::~syslog_backend() +{ + delete m_pImpl; +} + +//! The method installs the function object that maps application severity levels to Syslog levels +BOOST_LOG_API void syslog_backend::set_severity_mapper(severity_mapper_type const& mapper) +{ + m_pImpl->m_LevelMapper = mapper; +} + +//! The method writes the message to the sink +BOOST_LOG_API void syslog_backend::consume(record_view const& rec, string_type const& formatted_message) +{ + m_pImpl->send( + m_pImpl->m_LevelMapper.empty() ? syslog::info : m_pImpl->m_LevelMapper(rec), + formatted_message); +} + + +//! The method creates the backend implementation +BOOST_LOG_API void syslog_backend::construct(syslog::facility fac, syslog::impl_types use_impl, ip_versions ip_version, std::string const& ident) +{ +#ifdef BOOST_LOG_USE_NATIVE_SYSLOG + if (use_impl == syslog::native) + { + typedef implementation::native native_impl; + m_pImpl = new native_impl(fac, ident); + return; + } +#endif // BOOST_LOG_USE_NATIVE_SYSLOG + +#if !defined(BOOST_LOG_NO_ASIO) + typedef implementation::udp_socket_based udp_socket_based_impl; + switch (ip_version) + { + case v4: + m_pImpl = new udp_socket_based_impl(fac, asio::ip::udp::v4()); + break; + case v6: + m_pImpl = new udp_socket_based_impl(fac, asio::ip::udp::v6()); + break; + default: + BOOST_LOG_THROW_DESCR(setup_error, "Incorrect IP version specified"); + } +#endif +} + +#if !defined(BOOST_LOG_NO_ASIO) + +//! The method sets the local address which log records will be sent from. +BOOST_LOG_API void syslog_backend::set_local_address(std::string const& addr, unsigned short port) +{ +#if !defined(BOOST_LOG_NO_THREADS) + typedef implementation::udp_socket_based udp_socket_based_impl; + if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) + { + char service_name[std::numeric_limits< int >::digits10 + 3]; + boost::log::aux::snprintf(service_name, sizeof(service_name), "%d", static_cast< int >(port)); + + asio::ip::udp::endpoint local_address; + { + lock_guard< mutex > lock(impl->m_pService->m_Mutex); + asio::ip::udp::resolver::results_type results = impl->m_pService->m_HostNameResolver.resolve + ( + addr, + service_name, + asio::ip::resolver_base::address_configured | asio::ip::resolver_base::passive + ); + + local_address = *results.cbegin(); + } + + impl->m_pSocket.reset(new syslog_udp_socket(impl->m_pService->m_IOContext, impl->m_Protocol, local_address)); + } +#else + // Boost.ASIO requires threads for the host name resolver, + // so without threads we simply assume the string already contains IP address + set_local_address(boost::asio::ip::address::from_string(addr), port); +#endif // !defined(BOOST_LOG_NO_THREADS) +} +//! The method sets the local address which log records will be sent from. +BOOST_LOG_API void syslog_backend::set_local_address(boost::asio::ip::address const& addr, unsigned short port) +{ + typedef implementation::udp_socket_based udp_socket_based_impl; + if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) + { + impl->m_pSocket.reset(new syslog_udp_socket( + impl->m_pService->m_IOContext, impl->m_Protocol, asio::ip::udp::endpoint(addr, port))); + } +} + +//! The method sets the address of the remote host where log records will be sent to. +BOOST_LOG_API void syslog_backend::set_target_address(std::string const& addr, unsigned short port) +{ +#if !defined(BOOST_LOG_NO_THREADS) + typedef implementation::udp_socket_based udp_socket_based_impl; + if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) + { + char service_name[std::numeric_limits< int >::digits10 + 3]; + boost::log::aux::snprintf(service_name, sizeof(service_name), "%d", static_cast< int >(port)); + + asio::ip::udp::endpoint remote_address; + { + lock_guard< mutex > lock(impl->m_pService->m_Mutex); + asio::ip::udp::resolver::results_type results = impl->m_pService->m_HostNameResolver.resolve + ( + addr, + service_name, + asio::ip::resolver_query_base::address_configured + ); + + remote_address = *results.cbegin(); + } + + impl->m_TargetHost = remote_address; + } +#else + // Boost.ASIO requires threads for the host name resolver, + // so without threads we simply assume the string already contains IP address + set_target_address(boost::asio::ip::address::from_string(addr), port); +#endif // !defined(BOOST_LOG_NO_THREADS) +} +//! The method sets the address of the remote host where log records will be sent to. +BOOST_LOG_API void syslog_backend::set_target_address(boost::asio::ip::address const& addr, unsigned short port) +{ + typedef implementation::udp_socket_based udp_socket_based_impl; + if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) + { + impl->m_TargetHost = asio::ip::udp::endpoint(addr, port); + } +} + +#endif // !defined(BOOST_LOG_NO_ASIO) + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_WITHOUT_SYSLOG) diff --git a/src/boost/libs/log/src/text_file_backend.cpp b/src/boost/libs/log/src/text_file_backend.cpp new file mode 100644 index 00000000..50cef3b0 --- /dev/null +++ b/src/boost/libs/log/src/text_file_backend.cpp @@ -0,0 +1,1568 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 text_file_backend.cpp + * \author Andrey Semashev + * \date 09.06.2009 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <ctime> +#include <cctype> +#include <cwctype> +#include <ctime> +#include <cstdio> +#include <cstdlib> +#include <cstddef> +#include <list> +#include <string> +#include <locale> +#include <ostream> +#include <sstream> +#include <iterator> +#include <algorithm> +#include <stdexcept> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/cstdint.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/throw_exception.hpp> +#include <boost/mpl/if.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/system/error_code.hpp> +#include <boost/system/system_error.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/fstream.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/convenience.hpp> +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/list_hook.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/gregorian/gregorian_types.hpp> +#include <boost/spirit/home/qi/numeric/numeric_utils.hpp> +#include <boost/log/detail/singleton.hpp> +#include <boost/log/detail/light_function.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/attributes/time_traits.hpp> +#include <boost/log/sinks/auto_newline_mode.hpp> +#include <boost/log/sinks/text_file_backend.hpp> +#include "unique_ptr.hpp" + +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/thread/locks.hpp> +#include <boost/thread/mutex.hpp> +#endif // !defined(BOOST_LOG_NO_THREADS) + +#include <boost/log/detail/header.hpp> + +namespace qi = boost::spirit::qi; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + typedef filesystem::filesystem_error filesystem_error; + + //! A possible Boost.Filesystem extension - renames or moves the file to the target storage + inline void move_file( + filesystem::path const& from, + filesystem::path const& to) + { +#if defined(BOOST_WINDOWS_API) + // On Windows MoveFile already does what we need + filesystem::rename(from, to); +#else + // On POSIX rename fails if the target points to a different device + system::error_code ec; + filesystem::rename(from, to, ec); + if (ec) + { + if (BOOST_LIKELY(ec.value() == system::errc::cross_device_link)) + { + // Attempt to manually move the file instead + filesystem::copy_file(from, to); + filesystem::remove(from); + } + else + { + BOOST_THROW_EXCEPTION(filesystem_error("failed to move file to another location", from, to, ec)); + } + } +#endif + } + + typedef filesystem::path::string_type path_string_type; + typedef path_string_type::value_type path_char_type; + + //! An auxiliary traits that contain various constants and functions regarding string and character operations + template< typename CharT > + struct file_char_traits; + + template< > + struct file_char_traits< char > + { + typedef char char_type; + + static const char_type percent = '%'; + static const char_type number_placeholder = 'N'; + static const char_type day_placeholder = 'd'; + static const char_type month_placeholder = 'm'; + static const char_type year_placeholder = 'y'; + static const char_type full_year_placeholder = 'Y'; + static const char_type frac_sec_placeholder = 'f'; + static const char_type seconds_placeholder = 'S'; + static const char_type minutes_placeholder = 'M'; + static const char_type hours_placeholder = 'H'; + static const char_type space = ' '; + static const char_type plus = '+'; + static const char_type minus = '-'; + static const char_type zero = '0'; + static const char_type dot = '.'; + static const char_type newline = '\n'; + + static bool is_digit(char c) + { + using namespace std; + return (isdigit(c) != 0); + } + static std::string default_file_name_pattern() { return "%5N.log"; } + }; + +#ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + const file_char_traits< char >::char_type file_char_traits< char >::percent; + const file_char_traits< char >::char_type file_char_traits< char >::number_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::day_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::month_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::year_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::full_year_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::frac_sec_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::seconds_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::minutes_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::hours_placeholder; + const file_char_traits< char >::char_type file_char_traits< char >::space; + const file_char_traits< char >::char_type file_char_traits< char >::plus; + const file_char_traits< char >::char_type file_char_traits< char >::minus; + const file_char_traits< char >::char_type file_char_traits< char >::zero; + const file_char_traits< char >::char_type file_char_traits< char >::dot; + const file_char_traits< char >::char_type file_char_traits< char >::newline; +#endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + + template< > + struct file_char_traits< wchar_t > + { + typedef wchar_t char_type; + + static const char_type percent = L'%'; + static const char_type number_placeholder = L'N'; + static const char_type day_placeholder = L'd'; + static const char_type month_placeholder = L'm'; + static const char_type year_placeholder = L'y'; + static const char_type full_year_placeholder = L'Y'; + static const char_type frac_sec_placeholder = L'f'; + static const char_type seconds_placeholder = L'S'; + static const char_type minutes_placeholder = L'M'; + static const char_type hours_placeholder = L'H'; + static const char_type space = L' '; + static const char_type plus = L'+'; + static const char_type minus = L'-'; + static const char_type zero = L'0'; + static const char_type dot = L'.'; + static const char_type newline = L'\n'; + + static bool is_digit(wchar_t c) + { + using namespace std; + return (iswdigit(c) != 0); + } + static std::wstring default_file_name_pattern() { return L"%5N.log"; } + }; + +#ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::percent; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::number_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::day_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::month_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::year_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::full_year_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::frac_sec_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::seconds_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::minutes_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::hours_placeholder; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::space; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::plus; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::minus; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::zero; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::dot; + const file_char_traits< wchar_t >::char_type file_char_traits< wchar_t >::newline; +#endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE + + //! Date and time formatter + class date_and_time_formatter + { + public: + typedef path_string_type result_type; + + private: + typedef date_time::time_facet< posix_time::ptime, path_char_type > time_facet_type; + + private: + mutable time_facet_type m_Facet; + mutable std::basic_ostringstream< path_char_type > m_Stream; + + public: + //! Constructor + date_and_time_formatter() : m_Facet(1u) + { + } + //! Copy constructor + date_and_time_formatter(date_and_time_formatter const& that) : m_Facet(1u) + { + } + //! The method formats the current date and time according to the format string str and writes the result into it + path_string_type operator()(path_string_type const& pattern, unsigned int counter) const + { + m_Facet.format(pattern.c_str()); + m_Stream.str(path_string_type()); + // Note: the regular operator<< fails because std::use_facet fails to find the facet in the locale because + // the facet type in Boost.DateTime has hidden visibility. See this ticket: + // https://svn.boost.org/trac/boost/ticket/11707 + std::ostreambuf_iterator< path_char_type > sbuf_it(m_Stream); + m_Facet.put(sbuf_it, m_Stream, m_Stream.fill(), boost::log::attributes::local_time_traits::get_clock()); + if (m_Stream.good()) + { + return m_Stream.str(); + } + else + { + m_Stream.clear(); + return pattern; + } + } + + BOOST_DELETED_FUNCTION(date_and_time_formatter& operator= (date_and_time_formatter const&)) + }; + + //! The functor formats the file counter into the file name + class file_counter_formatter + { + public: + typedef path_string_type result_type; + + private: + //! The position in the pattern where the file counter placeholder is + path_string_type::size_type m_FileCounterPosition; + //! File counter width + std::streamsize m_Width; + //! The file counter formatting stream + mutable std::basic_ostringstream< path_char_type > m_Stream; + + public: + //! Initializing constructor + file_counter_formatter(path_string_type::size_type pos, unsigned int width) : + m_FileCounterPosition(pos), + m_Width(width) + { + typedef file_char_traits< path_char_type > traits_t; + m_Stream.fill(traits_t::zero); + } + //! Copy constructor + file_counter_formatter(file_counter_formatter const& that) : + m_FileCounterPosition(that.m_FileCounterPosition), + m_Width(that.m_Width) + { + m_Stream.fill(that.m_Stream.fill()); + } + + //! The function formats the file counter into the file name + path_string_type operator()(path_string_type const& pattern, unsigned int counter) const + { + path_string_type file_name = pattern; + + m_Stream.str(path_string_type()); + m_Stream.width(m_Width); + m_Stream << counter; + file_name.insert(m_FileCounterPosition, m_Stream.str()); + + return file_name; + } + + BOOST_DELETED_FUNCTION(file_counter_formatter& operator= (file_counter_formatter const&)) + }; + + //! The function returns the pattern as the file name + class empty_formatter + { + public: + typedef path_string_type result_type; + + private: + path_string_type m_Pattern; + + public: + //! Initializing constructor + explicit empty_formatter(path_string_type const& pattern) : m_Pattern(pattern) + { + } + //! Copy constructor + empty_formatter(empty_formatter const& that) : m_Pattern(that.m_Pattern) + { + } + + //! The function returns the pattern as the file name + path_string_type const& operator() (unsigned int) const + { + return m_Pattern; + } + + BOOST_DELETED_FUNCTION(empty_formatter& operator= (empty_formatter const&)) + }; + + //! The function parses the format placeholder for file counter + bool parse_counter_placeholder(path_string_type::const_iterator& it, path_string_type::const_iterator end, unsigned int& width) + { + typedef qi::extract_uint< unsigned int, 10, 1, -1 > width_extract; + typedef file_char_traits< path_char_type > traits_t; + if (it == end) + return false; + + path_char_type c = *it; + if (c == traits_t::zero || c == traits_t::space || c == traits_t::plus || c == traits_t::minus) + { + // Skip filler and alignment specification + ++it; + if (it == end) + return false; + c = *it; + } + + if (traits_t::is_digit(c)) + { + // Parse width + if (!width_extract::call(it, end, width)) + return false; + if (it == end) + return false; + c = *it; + } + + if (c == traits_t::dot) + { + // Skip precision + ++it; + while (it != end && traits_t::is_digit(*it)) + ++it; + if (it == end) + return false; + c = *it; + } + + if (c == traits_t::number_placeholder) + { + ++it; + return true; + } + + return false; + } + + //! The function matches the file name and the pattern + bool match_pattern(path_string_type const& file_name, path_string_type const& pattern, unsigned int& file_counter, bool& file_counter_parsed) + { + typedef qi::extract_uint< unsigned int, 10, 1, -1 > file_counter_extract; + typedef file_char_traits< path_char_type > traits_t; + + struct local + { + // Verifies that the string contains exactly n digits + static bool scan_digits(path_string_type::const_iterator& it, path_string_type::const_iterator end, std::ptrdiff_t n) + { + for (; n > 0; --n) + { + if (it == end) + return false; + path_char_type c = *it++; + if (!traits_t::is_digit(c)) + return false; + } + return true; + } + }; + + path_string_type::const_iterator + f_it = file_name.begin(), + f_end = file_name.end(), + p_it = pattern.begin(), + p_end = pattern.end(); + bool placeholder_expected = false; + while (f_it != f_end && p_it != p_end) + { + path_char_type p_c = *p_it, f_c = *f_it; + if (!placeholder_expected) + { + if (p_c == traits_t::percent) + { + placeholder_expected = true; + ++p_it; + } + else if (p_c == f_c) + { + ++p_it; + ++f_it; + } + else + return false; + } + else + { + switch (p_c) + { + case traits_t::percent: // An escaped '%' + if (p_c == f_c) + { + ++p_it; + ++f_it; + break; + } + else + return false; + + case traits_t::seconds_placeholder: // Date/time components with 2-digits width + case traits_t::minutes_placeholder: + case traits_t::hours_placeholder: + case traits_t::day_placeholder: + case traits_t::month_placeholder: + case traits_t::year_placeholder: + if (!local::scan_digits(f_it, f_end, 2)) + return false; + ++p_it; + break; + + case traits_t::full_year_placeholder: // Date/time components with 4-digits width + if (!local::scan_digits(f_it, f_end, 4)) + return false; + ++p_it; + break; + + case traits_t::frac_sec_placeholder: // Fraction seconds width is configuration-dependent + typedef posix_time::time_res_traits posix_resolution_traits; + if (!local::scan_digits(f_it, f_end, posix_resolution_traits::num_fractional_digits())) + { + return false; + } + ++p_it; + break; + + default: // This should be the file counter placeholder or some unsupported placeholder + { + path_string_type::const_iterator p = p_it; + unsigned int width = 0; + if (!parse_counter_placeholder(p, p_end, width)) + { + BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported placeholder used in pattern for file scanning")); + } + + // Find where the file number ends + path_string_type::const_iterator f = f_it; + if (!local::scan_digits(f, f_end, width)) + return false; + while (f != f_end && traits_t::is_digit(*f)) + ++f; + + if (!file_counter_extract::call(f_it, f, file_counter)) + return false; + + file_counter_parsed = true; + p_it = p; + } + break; + } + + placeholder_expected = false; + } + } + + if (p_it == p_end) + { + if (f_it != f_end) + { + // The actual file name may end with an additional counter + // that is added by the collector in case if file name clash + return local::scan_digits(f_it, f_end, std::distance(f_it, f_end)); + } + else + return true; + } + else + return false; + } + + //! The function parses file name pattern and splits it into path and filename and creates a function object that will generate the actual filename from the pattern + void parse_file_name_pattern(filesystem::path const& pattern, filesystem::path& storage_dir, filesystem::path& file_name_pattern, boost::log::aux::light_function< path_string_type (unsigned int) >& file_name_generator) + { + // Note: avoid calling Boost.Filesystem functions that involve path::codecvt() + // https://svn.boost.org/trac/boost/ticket/9119 + + typedef file_char_traits< path_char_type > traits_t; + + file_name_pattern = pattern.filename(); + path_string_type name_pattern = file_name_pattern.native(); + storage_dir = filesystem::absolute(pattern.parent_path()); + + // Let's try to find the file counter placeholder + unsigned int placeholder_count = 0; + unsigned int width = 0; + bool counter_found = false; + path_string_type::size_type counter_pos = 0; + path_string_type::const_iterator end = name_pattern.end(); + path_string_type::const_iterator it = name_pattern.begin(); + + do + { + it = std::find(it, end, traits_t::percent); + if (it == end) + break; + path_string_type::const_iterator placeholder_begin = it++; + if (it == end) + break; + if (*it == traits_t::percent) + { + // An escaped percent detected + ++it; + continue; + } + + ++placeholder_count; + + if (!counter_found) + { + path_string_type::const_iterator it2 = it; + if (parse_counter_placeholder(it2, end, width)) + { + // We've found the file counter placeholder in the pattern + counter_found = true; + counter_pos = placeholder_begin - name_pattern.begin(); + name_pattern.erase(counter_pos, it2 - placeholder_begin); + --placeholder_count; + it = name_pattern.begin() + counter_pos; + end = name_pattern.end(); + } + } + } + while (it != end); + + // Construct the formatter functor + if (placeholder_count > 0) + { + if (counter_found) + { + // Both counter and date/time placeholder in the pattern + file_name_generator = boost::bind(date_and_time_formatter(), + boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1), _1); + } + else + { + // Only date/time placeholders in the pattern + file_name_generator = boost::bind(date_and_time_formatter(), name_pattern, _1); + } + } + else if (counter_found) + { + // Only counter placeholder in the pattern + file_name_generator = boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1); + } + else + { + // No placeholders detected + file_name_generator = empty_formatter(name_pattern); + } + } + + + class file_collector_repository; + + //! Type of the hook used for sequencing file collectors + typedef intrusive::list_base_hook< + intrusive::link_mode< intrusive::safe_link > + > file_collector_hook; + + //! Log file collector implementation + class file_collector : + public file::collector, + public file_collector_hook, + public enable_shared_from_this< file_collector > + { + private: + //! Information about a single stored file + struct file_info + { + uintmax_t m_Size; + std::time_t m_TimeStamp; + filesystem::path m_Path; + }; + //! A list of the stored files + typedef std::list< file_info > file_list; + //! The string type compatible with the universal path type + typedef filesystem::path::string_type path_string_type; + + private: + //! A reference to the repository this collector belongs to + shared_ptr< file_collector_repository > m_pRepository; + +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + mutex m_Mutex; +#endif // !defined(BOOST_LOG_NO_THREADS) + + //! Total file size upper limit + uintmax_t m_MaxSize; + //! Free space lower limit + uintmax_t m_MinFreeSpace; + //! File count upper limit + uintmax_t m_MaxFiles; + + //! The current path at the point when the collector is created + /* + * The special member is required to calculate absolute paths with no + * dependency on the current path for the application, which may change + */ + const filesystem::path m_BasePath; + //! Target directory to store files to + filesystem::path m_StorageDir; + + //! The list of stored files + file_list m_Files; + //! Total size of the stored files + uintmax_t m_TotalSize; + + public: + //! Constructor + file_collector( + shared_ptr< file_collector_repository > const& repo, + filesystem::path const& target_dir, + uintmax_t max_size, + uintmax_t min_free_space, + uintmax_t max_files); + + //! Destructor + ~file_collector(); + + //! The function stores the specified file in the storage + void store_file(filesystem::path const& file_name); + + //! Scans the target directory for the files that have already been stored + uintmax_t scan_for_files( + file::scan_method method, filesystem::path const& pattern, unsigned int* counter); + + //! The function updates storage restrictions + void update(uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files); + + //! The function checks if the directory is governed by this collector + bool is_governed(filesystem::path const& dir) const + { + return filesystem::equivalent(m_StorageDir, dir); + } + + private: + //! Makes relative path absolute with respect to the base path + filesystem::path make_absolute(filesystem::path const& p) + { + return filesystem::absolute(p, m_BasePath); + } + //! Acquires file name string from the path + static path_string_type filename_string(filesystem::path const& p) + { + return p.filename().string< path_string_type >(); + } + }; + + + //! The singleton of the list of file collectors + class file_collector_repository : + public log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > + { + private: + //! Base type + typedef log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > > base_type; + +#if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS) + friend class log::aux::lazy_singleton< file_collector_repository, shared_ptr< file_collector_repository > >; +#else + friend class base_type; +#endif + + //! The type of the list of collectors + typedef intrusive::list< + file_collector, + intrusive::base_hook< file_collector_hook > + > file_collectors; + + private: +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex + mutex m_Mutex; +#endif // !defined(BOOST_LOG_NO_THREADS) + //! The list of file collectors + file_collectors m_Collectors; + + public: + //! Finds or creates a file collector + shared_ptr< file::collector > get_collector( + filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files); + + //! Removes the file collector from the list + void remove_collector(file_collector* p); + + private: + //! Initializes the singleton instance + static void init_instance() + { + base_type::get_instance() = boost::make_shared< file_collector_repository >(); + } + }; + + //! Constructor + file_collector::file_collector( + shared_ptr< file_collector_repository > const& repo, + filesystem::path const& target_dir, + uintmax_t max_size, + uintmax_t min_free_space, + uintmax_t max_files + ) : + m_pRepository(repo), + m_MaxSize(max_size), + m_MinFreeSpace(min_free_space), + m_MaxFiles(max_files), + m_BasePath(filesystem::current_path()), + m_TotalSize(0) + { + m_StorageDir = make_absolute(target_dir); + filesystem::create_directories(m_StorageDir); + } + + //! Destructor + file_collector::~file_collector() + { + m_pRepository->remove_collector(this); + } + + //! The function stores the specified file in the storage + void file_collector::store_file(filesystem::path const& src_path) + { + // NOTE FOR THE FOLLOWING CODE: + // Avoid using Boost.Filesystem functions that would call path::codecvt(). store_file() can be called + // at process termination, and the global codecvt facet can already be destroyed at this point. + // https://svn.boost.org/trac/boost/ticket/8642 + + // Let's construct the new file name + file_info info; + info.m_TimeStamp = filesystem::last_write_time(src_path); + info.m_Size = filesystem::file_size(src_path); + + filesystem::path file_name_path = src_path.filename(); + path_string_type file_name = file_name_path.native(); + info.m_Path = m_StorageDir / file_name_path; + + // Check if the file is already in the target directory + filesystem::path src_dir = src_path.has_parent_path() ? + filesystem::system_complete(src_path.parent_path()) : + m_BasePath; + const bool is_in_target_dir = filesystem::equivalent(src_dir, m_StorageDir); + if (!is_in_target_dir) + { + if (filesystem::exists(info.m_Path)) + { + // If the file already exists, try to mangle the file name + // to ensure there's no conflict. I'll need to make this customizable some day. + file_counter_formatter formatter(file_name.size(), 5); + unsigned int n = 0; + while (true) + { + path_string_type alt_file_name = formatter(file_name, n); + info.m_Path = m_StorageDir / filesystem::path(alt_file_name); + if (!filesystem::exists(info.m_Path)) + break; + + if (BOOST_UNLIKELY(n == (std::numeric_limits< unsigned int >::max)())) + { + BOOST_THROW_EXCEPTION(filesystem_error( + "Target file exists and an unused fallback file name could not be found", + info.m_Path, + system::error_code(system::errc::io_error, system::generic_category()))); + } + + ++n; + } + } + + // The directory should have been created in constructor, but just in case it got deleted since then... + filesystem::create_directories(m_StorageDir); + } + + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + + file_list::iterator it = m_Files.begin(); + const file_list::iterator end = m_Files.end(); + if (is_in_target_dir) + { + // If the sink writes log file into the target dir (is_in_target_dir == true), it is possible that after scanning + // an old file entry refers to the file that is picked up by the sink for writing. Later on, the sink attempts + // to store the file in the storage. At best, this would result in duplicate file entries. At worst, if the storage + // limits trigger a deletion and this file get deleted, we may have an entry that refers to no actual file. In any case, + // the total size of files in the storage will be incorrect. Here we work around this problem and simply remove + // the old file entry without removing the file. The entry will be re-added to the list later. + while (it != end) + { + system::error_code ec; + if (filesystem::equivalent(it->m_Path, info.m_Path, ec)) + { + m_TotalSize -= it->m_Size; + m_Files.erase(it); + break; + } + else + { + ++it; + } + } + + it = m_Files.begin(); + } + + // Check if an old file should be erased + uintmax_t free_space = m_MinFreeSpace ? filesystem::space(m_StorageDir).available : static_cast< uintmax_t >(0); + while (it != end && + (m_TotalSize + info.m_Size > m_MaxSize || (m_MinFreeSpace && m_MinFreeSpace > free_space) || m_MaxFiles <= m_Files.size())) + { + file_info& old_info = *it; + system::error_code ec; + filesystem::file_status status = filesystem::status(old_info.m_Path, ec); + + if (status.type() == filesystem::regular_file) + { + try + { + filesystem::remove(old_info.m_Path); + // Free space has to be queried as it may not increase equally + // to the erased file size on compressed filesystems + if (m_MinFreeSpace) + free_space = filesystem::space(m_StorageDir).available; + m_TotalSize -= old_info.m_Size; + m_Files.erase(it++); + } + catch (system::system_error&) + { + // Can't erase the file. Maybe it's locked? Never mind... + ++it; + } + } + else + { + // If it's not a file or is absent, just remove it from the list + m_TotalSize -= old_info.m_Size; + m_Files.erase(it++); + } + } + + if (!is_in_target_dir) + { + // Move/rename the file to the target storage + move_file(src_path, info.m_Path); + } + + m_Files.push_back(info); + m_TotalSize += info.m_Size; + } + + //! Scans the target directory for the files that have already been stored + uintmax_t file_collector::scan_for_files( + file::scan_method method, filesystem::path const& pattern, unsigned int* counter) + { + uintmax_t file_count = 0; + if (method != file::no_scan) + { + filesystem::path dir = m_StorageDir; + path_string_type mask; + if (method == file::scan_matching) + { + mask = filename_string(pattern); + if (pattern.has_parent_path()) + dir = make_absolute(pattern.parent_path()); + } + else + { + counter = NULL; + } + + system::error_code ec; + filesystem::file_status status = filesystem::status(dir, ec); + if (status.type() == filesystem::directory_file) + { + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + + if (counter) + *counter = 0; + + file_list files; + filesystem::directory_iterator it(dir), end; + uintmax_t total_size = 0; + for (; it != end; ++it) + { + filesystem::directory_entry const& dir_entry = *it; + file_info info; + info.m_Path = dir_entry.path(); + status = dir_entry.status(ec); + if (status.type() == filesystem::regular_file) + { + // Check that there are no duplicates in the resulting list + struct local + { + static bool equivalent(filesystem::path const& left, file_info const& right) + { + return filesystem::equivalent(left, right.m_Path); + } + }; + if (std::find_if(m_Files.begin(), m_Files.end(), + boost::bind(&local::equivalent, boost::cref(info.m_Path), _1)) == m_Files.end()) + { + // Check that the file name matches the pattern + unsigned int file_number = 0; + bool file_number_parsed = false; + if (method != file::scan_matching || + match_pattern(filename_string(info.m_Path), mask, file_number, file_number_parsed)) + { + info.m_Size = filesystem::file_size(info.m_Path); + total_size += info.m_Size; + info.m_TimeStamp = filesystem::last_write_time(info.m_Path); + files.push_back(info); + ++file_count; + + // Test that the file_number >= *counter accounting for the integer overflow + if (file_number_parsed && counter != NULL && (file_number - *counter) < ((~0u) ^ ((~0u) >> 1))) + *counter = file_number + 1u; + } + } + } + } + + // Sort files chronologically + m_Files.splice(m_Files.end(), files); + m_TotalSize += total_size; + m_Files.sort(boost::bind(&file_info::m_TimeStamp, _1) < boost::bind(&file_info::m_TimeStamp, _2)); + } + } + + return file_count; + } + + //! The function updates storage restrictions + void file_collector::update(uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files) + { + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + + m_MaxSize = (std::min)(m_MaxSize, max_size); + m_MinFreeSpace = (std::max)(m_MinFreeSpace, min_free_space); + m_MaxFiles = (std::min)(m_MaxFiles, max_files); + } + + + //! Finds or creates a file collector + shared_ptr< file::collector > file_collector_repository::get_collector( + filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files) + { + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + + file_collectors::iterator it = std::find_if(m_Collectors.begin(), m_Collectors.end(), + boost::bind(&file_collector::is_governed, _1, boost::cref(target_dir))); + shared_ptr< file_collector > p; + if (it != m_Collectors.end()) try + { + // This may throw if the collector is being currently destroyed + p = it->shared_from_this(); + p->update(max_size, min_free_space, max_files); + } + catch (bad_weak_ptr&) + { + } + + if (!p) + { + p = boost::make_shared< file_collector >( + file_collector_repository::get(), target_dir, max_size, min_free_space, max_files); + m_Collectors.push_back(*p); + } + + return p; + } + + //! Removes the file collector from the list + void file_collector_repository::remove_collector(file_collector* p) + { + BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + m_Collectors.erase(m_Collectors.iterator_to(*p)); + } + + //! Checks if the time point is valid + void check_time_point_validity(unsigned char hour, unsigned char minute, unsigned char second) + { + if (BOOST_UNLIKELY(hour >= 24)) + { + std::ostringstream strm; + strm << "Time point hours value is out of range: " << static_cast< unsigned int >(hour); + BOOST_THROW_EXCEPTION(std::out_of_range(strm.str())); + } + if (BOOST_UNLIKELY(minute >= 60)) + { + std::ostringstream strm; + strm << "Time point minutes value is out of range: " << static_cast< unsigned int >(minute); + BOOST_THROW_EXCEPTION(std::out_of_range(strm.str())); + } + if (BOOST_UNLIKELY(second >= 60)) + { + std::ostringstream strm; + strm << "Time point seconds value is out of range: " << static_cast< unsigned int >(second); + BOOST_THROW_EXCEPTION(std::out_of_range(strm.str())); + } + } + +} // namespace + +namespace file { + +namespace aux { + + //! Creates and returns a file collector with the specified parameters + BOOST_LOG_API shared_ptr< collector > make_collector( + filesystem::path const& target_dir, + uintmax_t max_size, + uintmax_t min_free_space, + uintmax_t max_files) + { + return file_collector_repository::get()->get_collector(target_dir, max_size, min_free_space, max_files); + } + +} // namespace aux + +//! Creates a rotation time point of every day at the specified time +BOOST_LOG_API rotation_at_time_point::rotation_at_time_point( + unsigned char hour, + unsigned char minute, + unsigned char second +) : + m_DayKind(not_specified), + m_Day(0), + m_Hour(hour), + m_Minute(minute), + m_Second(second), + m_Previous(date_time::not_a_date_time) +{ + check_time_point_validity(hour, minute, second); +} + +//! Creates a rotation time point of each specified weekday at the specified time +BOOST_LOG_API rotation_at_time_point::rotation_at_time_point( + date_time::weekdays wday, + unsigned char hour, + unsigned char minute, + unsigned char second +) : + m_DayKind(weekday), + m_Day(static_cast< unsigned char >(wday)), + m_Hour(hour), + m_Minute(minute), + m_Second(second), + m_Previous(date_time::not_a_date_time) +{ + check_time_point_validity(hour, minute, second); +} + +//! Creates a rotation time point of each specified day of month at the specified time +BOOST_LOG_API rotation_at_time_point::rotation_at_time_point( + gregorian::greg_day mday, + unsigned char hour, + unsigned char minute, + unsigned char second +) : + m_DayKind(monthday), + m_Day(static_cast< unsigned char >(mday.as_number())), + m_Hour(hour), + m_Minute(minute), + m_Second(second), + m_Previous(date_time::not_a_date_time) +{ + check_time_point_validity(hour, minute, second); +} + +//! Checks if it's time to rotate the file +BOOST_LOG_API bool rotation_at_time_point::operator()() const +{ + bool result = false; + posix_time::time_duration rotation_time( + static_cast< posix_time::time_duration::hour_type >(m_Hour), + static_cast< posix_time::time_duration::min_type >(m_Minute), + static_cast< posix_time::time_duration::sec_type >(m_Second)); + posix_time::ptime now = posix_time::second_clock::local_time(); + + if (m_Previous.is_special()) + { + m_Previous = now; + return false; + } + + const bool time_of_day_passed = rotation_time.total_seconds() <= m_Previous.time_of_day().total_seconds(); + switch (m_DayKind) + { + case not_specified: + { + // The rotation takes place every day at the specified time + gregorian::date previous_date = m_Previous.date(); + if (time_of_day_passed) + previous_date += gregorian::days(1); + posix_time::ptime next(previous_date, rotation_time); + result = (now >= next); + } + break; + + case weekday: + { + // The rotation takes place on the specified week day at the specified time + gregorian::date previous_date = m_Previous.date(), next_date = previous_date; + int weekday = m_Day, previous_weekday = static_cast< int >(previous_date.day_of_week().as_number()); + next_date += gregorian::days(weekday - previous_weekday); + if (weekday < previous_weekday || (weekday == previous_weekday && time_of_day_passed)) + { + next_date += gregorian::weeks(1); + } + + posix_time::ptime next(next_date, rotation_time); + result = (now >= next); + } + break; + + case monthday: + { + // The rotation takes place on the specified day of month at the specified time + gregorian::date previous_date = m_Previous.date(); + gregorian::date::day_type monthday = static_cast< gregorian::date::day_type >(m_Day), + previous_monthday = previous_date.day(); + gregorian::date next_date(previous_date.year(), previous_date.month(), monthday); + if (monthday < previous_monthday || (monthday == previous_monthday && time_of_day_passed)) + { + next_date += gregorian::months(1); + } + + posix_time::ptime next(next_date, rotation_time); + result = (now >= next); + } + break; + + default: + break; + } + + if (result) + m_Previous = now; + + return result; +} + +//! Checks if it's time to rotate the file +BOOST_LOG_API bool rotation_at_time_interval::operator()() const +{ + bool result = false; + posix_time::ptime now = posix_time::second_clock::universal_time(); + if (m_Previous.is_special()) + { + m_Previous = now; + return false; + } + + result = (now - m_Previous) >= m_Interval; + + if (result) + m_Previous = now; + + return result; +} + +} // namespace file + +//////////////////////////////////////////////////////////////////////////////// +// File sink backend implementation +//////////////////////////////////////////////////////////////////////////////// +//! Sink implementation data +struct text_file_backend::implementation +{ + //! File open mode + std::ios_base::openmode m_FileOpenMode; + + //! File name pattern + filesystem::path m_FileNamePattern; + //! Directory to store files in + filesystem::path m_StorageDir; + //! File name generator (according to m_FileNamePattern) + boost::log::aux::light_function< path_string_type (unsigned int) > m_FileNameGenerator; + + //! Target file name pattern + filesystem::path m_TargetFileNamePattern; + //! Target directory to store files in + filesystem::path m_TargetStorageDir; + //! Target file name generator (according to m_TargetFileNamePattern) + boost::log::aux::light_function< path_string_type (unsigned int) > m_TargetFileNameGenerator; + + //! Stored files counter + unsigned int m_FileCounter; + + //! Current file name + filesystem::path m_FileName; + //! File stream + filesystem::ofstream m_File; + //! Characters written + uintmax_t m_CharactersWritten; + + //! File collector functional object + shared_ptr< file::collector > m_pFileCollector; + //! File open handler + open_handler_type m_OpenHandler; + //! File close handler + close_handler_type m_CloseHandler; + + //! The maximum temp file size, in characters written to the stream + uintmax_t m_FileRotationSize; + //! Time-based rotation predicate + time_based_rotation_predicate m_TimeBasedRotation; + //! Indicates whether to append a trailing newline after every log record + auto_newline_mode m_AutoNewlineMode; + //! The flag shows if every written record should be flushed + bool m_AutoFlush; + //! The flag indicates whether the final rotation should be performed + bool m_FinalRotationEnabled; + + implementation(uintmax_t rotation_size, auto_newline_mode auto_newline, bool auto_flush, bool enable_final_rotation) : + m_FileOpenMode(std::ios_base::trunc | std::ios_base::out), + m_FileCounter(0), + m_CharactersWritten(0), + m_FileRotationSize(rotation_size), + m_AutoNewlineMode(auto_newline), + m_AutoFlush(auto_flush), + m_FinalRotationEnabled(enable_final_rotation) + { + } +}; + +//! Constructor. No streams attached to the constructed backend, auto flush feature disabled. +BOOST_LOG_API text_file_backend::text_file_backend() +{ + construct(log::aux::empty_arg_list()); +} + +//! Destructor +BOOST_LOG_API text_file_backend::~text_file_backend() +{ + try + { + // Attempt to put the temporary file into storage + if (m_pImpl->m_FinalRotationEnabled && m_pImpl->m_File.is_open() && m_pImpl->m_CharactersWritten > 0) + rotate_file(); + } + catch (...) + { + } + + delete m_pImpl; +} + +//! Constructor implementation +BOOST_LOG_API void text_file_backend::construct( + filesystem::path const& pattern, + filesystem::path const& target_file_name, + std::ios_base::openmode mode, + uintmax_t rotation_size, + time_based_rotation_predicate const& time_based_rotation, + auto_newline_mode auto_newline, + bool auto_flush, + bool enable_final_rotation) +{ + m_pImpl = new implementation(rotation_size, auto_newline, auto_flush, enable_final_rotation); + set_file_name_pattern_internal(pattern); + set_target_file_name_pattern_internal(target_file_name); + set_time_based_rotation(time_based_rotation); + set_open_mode(mode); +} + +//! The method sets maximum file size. +BOOST_LOG_API void text_file_backend::set_rotation_size(uintmax_t size) +{ + m_pImpl->m_FileRotationSize = size; +} + +//! The method sets the maximum time interval between file rotations. +BOOST_LOG_API void text_file_backend::set_time_based_rotation(time_based_rotation_predicate const& predicate) +{ + m_pImpl->m_TimeBasedRotation = predicate; +} + +//! The method allows to enable or disable log file rotation on sink destruction. +BOOST_LOG_API void text_file_backend::enable_final_rotation(bool enable) +{ + m_pImpl->m_FinalRotationEnabled = enable; +} + +//! Sets the flag to automatically flush write buffers of the file being written after each log record. +BOOST_LOG_API void text_file_backend::auto_flush(bool enable) +{ + m_pImpl->m_AutoFlush = enable; +} + +//! Selects whether a trailing newline should be automatically inserted after every log record. +BOOST_LOG_API void text_file_backend::set_auto_newline_mode(auto_newline_mode mode) +{ + m_pImpl->m_AutoNewlineMode = mode; +} + +//! The method writes the message to the sink +BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_type const& formatted_message) +{ + typedef file_char_traits< string_type::value_type > traits_t; + + filesystem::path prev_file_name; + bool use_prev_file_name = false; + if (BOOST_UNLIKELY(!m_pImpl->m_File.good())) + { + // The file stream is not operational. One possible reason is that there is no more free space + // on the file system. In this case it is possible that this log record will fail to be written as well, + // leaving the newly creted file empty. Eventually this results in lots of empty log files. + // We should take precautions to avoid this. https://svn.boost.org/trac/boost/ticket/11016 + prev_file_name = m_pImpl->m_FileName; + close_file(); + + system::error_code ec; + uintmax_t size = filesystem::file_size(prev_file_name, ec); + if (!!ec || size == 0) + { + // To reuse the empty file avoid re-generating the new file name later + use_prev_file_name = true; + } + else if (!!m_pImpl->m_pFileCollector) + { + // Complete file rotation + m_pImpl->m_pFileCollector->store_file(prev_file_name); + } + } + else if + ( + m_pImpl->m_File.is_open() && + ( + m_pImpl->m_CharactersWritten + formatted_message.size() >= m_pImpl->m_FileRotationSize || + (!m_pImpl->m_TimeBasedRotation.empty() && m_pImpl->m_TimeBasedRotation()) + ) + ) + { + rotate_file(); + } + + if (!m_pImpl->m_File.is_open()) + { + filesystem::path new_file_name; + if (!use_prev_file_name) + new_file_name = m_pImpl->m_StorageDir / m_pImpl->m_FileNameGenerator(m_pImpl->m_FileCounter++); + else + prev_file_name.swap(new_file_name); + + filesystem::create_directories(new_file_name.parent_path()); + + m_pImpl->m_File.open(new_file_name, m_pImpl->m_FileOpenMode); + if (BOOST_UNLIKELY(!m_pImpl->m_File.is_open())) + { + BOOST_THROW_EXCEPTION(filesystem_error( + "Failed to open file for writing", + new_file_name, + system::error_code(system::errc::io_error, system::generic_category()))); + } + m_pImpl->m_FileName.swap(new_file_name); + + if (!m_pImpl->m_OpenHandler.empty()) + m_pImpl->m_OpenHandler(m_pImpl->m_File); + + m_pImpl->m_CharactersWritten = static_cast< std::streamoff >(m_pImpl->m_File.tellp()); + } + + m_pImpl->m_File.write(formatted_message.data(), static_cast< std::streamsize >(formatted_message.size())); + m_pImpl->m_CharactersWritten += formatted_message.size(); + + if (m_pImpl->m_AutoNewlineMode != disabled_auto_newline) + { + if (m_pImpl->m_AutoNewlineMode == always_insert || formatted_message.empty() || *formatted_message.rbegin() != traits_t::newline) + { + m_pImpl->m_File.put(traits_t::newline); + ++m_pImpl->m_CharactersWritten; + } + } + + if (m_pImpl->m_AutoFlush) + m_pImpl->m_File.flush(); +} + +//! The method flushes the currently open log file +BOOST_LOG_API void text_file_backend::flush() +{ + if (m_pImpl->m_File.is_open()) + m_pImpl->m_File.flush(); +} + +//! The method sets file name pattern +BOOST_LOG_API void text_file_backend::set_file_name_pattern_internal(filesystem::path const& pattern) +{ + typedef file_char_traits< path_char_type > traits_t; + + parse_file_name_pattern + ( + !pattern.empty() ? pattern : filesystem::path(traits_t::default_file_name_pattern()), + m_pImpl->m_StorageDir, + m_pImpl->m_FileNamePattern, + m_pImpl->m_FileNameGenerator + ); +} + +//! The method sets target file name pattern +BOOST_LOG_API void text_file_backend::set_target_file_name_pattern_internal(filesystem::path const& pattern) +{ + if (!pattern.empty()) + { + parse_file_name_pattern(pattern, m_pImpl->m_TargetStorageDir, m_pImpl->m_TargetFileNamePattern, m_pImpl->m_TargetFileNameGenerator); + } + else + { + m_pImpl->m_TargetStorageDir.clear(); + m_pImpl->m_TargetFileNamePattern.clear(); + m_pImpl->m_TargetFileNameGenerator.clear(); + } +} + +//! Closes the currently open file +void text_file_backend::close_file() +{ + if (m_pImpl->m_File.is_open()) + { + if (!m_pImpl->m_CloseHandler.empty()) + { + // Rationale: We should call the close handler even if the stream is !good() because + // writing the footer may not be the only thing the handler does. However, there is + // a chance that the file had become writable since the last failure (e.g. there was + // no space left to write the last record, but it got freed since then), so if the handler + // attempts to write a footer it may succeed now. For this reason we clear the stream state + // and let the handler have a try. + m_pImpl->m_File.clear(); + m_pImpl->m_CloseHandler(m_pImpl->m_File); + } + + m_pImpl->m_File.close(); + } + + m_pImpl->m_File.clear(); + m_pImpl->m_CharactersWritten = 0; + m_pImpl->m_FileName.clear(); +} + +//! The method rotates the file +BOOST_LOG_API void text_file_backend::rotate_file() +{ + filesystem::path prev_file_name = m_pImpl->m_FileName; + close_file(); + + if (!!m_pImpl->m_TargetFileNameGenerator) + { + filesystem::path new_file_name; + new_file_name = m_pImpl->m_TargetStorageDir / m_pImpl->m_TargetFileNameGenerator(m_pImpl->m_FileCounter); + + if (new_file_name != prev_file_name) + { + filesystem::create_directories(new_file_name.parent_path()); + move_file(prev_file_name, new_file_name); + + prev_file_name.swap(new_file_name); + } + } + + if (!!m_pImpl->m_pFileCollector) + { + // Check if the file has not been deleted by another process + system::error_code ec; + filesystem::file_status status = filesystem::status(prev_file_name, ec); + if (status.type() == filesystem::regular_file) + m_pImpl->m_pFileCollector->store_file(prev_file_name); + } +} + +//! The method sets the file open mode +BOOST_LOG_API void text_file_backend::set_open_mode(std::ios_base::openmode mode) +{ + mode |= std::ios_base::out; + mode &= ~std::ios_base::in; + if ((mode & (std::ios_base::trunc | std::ios_base::app)) == 0) + mode |= std::ios_base::trunc; + m_pImpl->m_FileOpenMode = mode; +} + +//! The method sets file collector +BOOST_LOG_API void text_file_backend::set_file_collector(shared_ptr< file::collector > const& collector) +{ + m_pImpl->m_pFileCollector = collector; +} + +//! The method sets file open handler +BOOST_LOG_API void text_file_backend::set_open_handler(open_handler_type const& handler) +{ + m_pImpl->m_OpenHandler = handler; +} + +//! The method sets file close handler +BOOST_LOG_API void text_file_backend::set_close_handler(close_handler_type const& handler) +{ + m_pImpl->m_CloseHandler = handler; +} + +//! The method returns name of the currently open log file. If no file is open, returns an empty path. +BOOST_LOG_API filesystem::path text_file_backend::get_current_file_name() const +{ + return m_pImpl->m_FileName; +} + +//! Performs scanning of the target directory for log files +BOOST_LOG_API uintmax_t text_file_backend::scan_for_files(file::scan_method method, bool update_counter) +{ + if (BOOST_LIKELY(!!m_pImpl->m_pFileCollector)) + { + unsigned int* counter = update_counter ? &m_pImpl->m_FileCounter : static_cast< unsigned int* >(NULL); + return m_pImpl->m_pFileCollector->scan_for_files + ( + method, + m_pImpl->m_TargetFileNamePattern.empty() ? m_pImpl->m_FileNamePattern : m_pImpl->m_TargetFileNamePattern, + counter + ); + } + else + { + BOOST_LOG_THROW_DESCR(setup_error, "File collector is not set"); + } +} + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/text_multifile_backend.cpp b/src/boost/libs/log/src/text_multifile_backend.cpp new file mode 100644 index 00000000..550dd57f --- /dev/null +++ b/src/boost/libs/log/src/text_multifile_backend.cpp @@ -0,0 +1,114 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 text_multifile_backend.cpp + * \author Andrey Semashev + * \date 09.06.2009 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <ios> +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/fstream.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/log/detail/parameter_tools.hpp> +#include <boost/log/sinks/auto_newline_mode.hpp> +#include <boost/log/sinks/text_multifile_backend.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +//! Sink implementation data +struct text_multifile_backend::implementation +{ + //! File name composer + file_name_composer_type m_FileNameComposer; + //! Base path for absolute path composition + const filesystem::path m_BasePath; + //! File stream + filesystem::ofstream m_File; + //! Indicates whether to append a trailing newline after every log record + auto_newline_mode m_AutoNewlineMode; + + explicit implementation(auto_newline_mode auto_newline) : + m_BasePath(filesystem::current_path()), + m_AutoNewlineMode(auto_newline) + { + } + + //! Makes relative path absolute with respect to the base path + filesystem::path make_absolute(filesystem::path const& p) + { + return filesystem::absolute(p, m_BasePath); + } +}; + +//! Default constructor +BOOST_LOG_API text_multifile_backend::text_multifile_backend() +{ + construct(log::aux::empty_arg_list()); +} + +//! Constructor implementation +BOOST_LOG_API void text_multifile_backend::construct(auto_newline_mode auto_newline) +{ + m_pImpl = new implementation(auto_newline); +} + +//! Destructor +BOOST_LOG_API text_multifile_backend::~text_multifile_backend() +{ + delete m_pImpl; +} + +//! The method sets the file name composer +BOOST_LOG_API void text_multifile_backend::set_file_name_composer_internal(file_name_composer_type const& composer) +{ + m_pImpl->m_FileNameComposer = composer; +} + +//! Selects whether a trailing newline should be automatically inserted after every log record. +BOOST_LOG_API void text_multifile_backend::set_auto_newline_mode(auto_newline_mode mode) +{ + m_pImpl->m_AutoNewlineMode = mode; +} + +//! The method writes the message to the sink +BOOST_LOG_API void text_multifile_backend::consume(record_view const& rec, string_type const& formatted_message) +{ + if (BOOST_LIKELY(!m_pImpl->m_FileNameComposer.empty())) + { + filesystem::path file_name = m_pImpl->make_absolute(m_pImpl->m_FileNameComposer(rec)); + filesystem::create_directories(file_name.parent_path()); + m_pImpl->m_File.open(file_name, std::ios_base::out | std::ios_base::app); + if (BOOST_LIKELY(m_pImpl->m_File.is_open())) + { + m_pImpl->m_File.write(formatted_message.data(), static_cast< std::streamsize >(formatted_message.size())); + if (m_pImpl->m_AutoNewlineMode != disabled_auto_newline) + { + if (m_pImpl->m_AutoNewlineMode == always_insert || formatted_message.empty() || *formatted_message.rbegin() != static_cast< string_type::value_type >('\n')) + m_pImpl->m_File.put(static_cast< string_type::value_type >('\n')); + } + m_pImpl->m_File.close(); + } + } +} + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/text_ostream_backend.cpp b/src/boost/libs/log/src/text_ostream_backend.cpp new file mode 100644 index 00000000..047b0c00 --- /dev/null +++ b/src/boost/libs/log/src/text_ostream_backend.cpp @@ -0,0 +1,164 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 text_ostream_backend.cpp + * \author Andrey Semashev + * \date 19.04.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <vector> +#include <algorithm> +#include <boost/log/detail/parameter_tools.hpp> +#include <boost/log/sinks/auto_newline_mode.hpp> +#include <boost/log/sinks/text_ostream_backend.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +//! Sink implementation +template< typename CharT > +struct basic_text_ostream_backend< CharT >::implementation +{ + //! Type of the container that holds all aggregated streams + typedef std::vector< shared_ptr< stream_type > > ostream_sequence; + + //! Output stream list + ostream_sequence m_Streams; + //! Indicates whether to append a trailing newline after every log record + auto_newline_mode m_AutoNewlineMode; + //! Auto-flush flag + bool m_fAutoFlush; + + implementation(auto_newline_mode auto_newline, bool auto_flush) : + m_AutoNewlineMode(auto_newline), + m_fAutoFlush(auto_flush) + { + } +}; + + +//! Constructor +template< typename CharT > +BOOST_LOG_API basic_text_ostream_backend< CharT >::basic_text_ostream_backend() +{ + construct(log::aux::empty_arg_list()); +} + +//! Constructor implementation +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::construct(auto_newline_mode auto_newline, bool auto_flush) +{ + m_pImpl = new implementation(auto_newline, auto_flush); +} + +//! Destructor (just to make it link from the shared library) +template< typename CharT > +BOOST_LOG_API basic_text_ostream_backend< CharT >::~basic_text_ostream_backend() +{ + delete m_pImpl; +} + +//! Selects whether a trailing newline should be automatically inserted after every log record. +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::set_auto_newline_mode(auto_newline_mode mode) +{ + m_pImpl->m_AutoNewlineMode = mode; +} + +//! The method adds a new stream to the sink +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::add_stream(shared_ptr< stream_type > const& strm) +{ + typename implementation::ostream_sequence::iterator it = + std::find(m_pImpl->m_Streams.begin(), m_pImpl->m_Streams.end(), strm); + if (it == m_pImpl->m_Streams.end()) + { + m_pImpl->m_Streams.push_back(strm); + } +} + +//! The method removes a stream from the sink +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::remove_stream(shared_ptr< stream_type > const& strm) +{ + typename implementation::ostream_sequence::iterator it = + std::find(m_pImpl->m_Streams.begin(), m_pImpl->m_Streams.end(), strm); + if (it != m_pImpl->m_Streams.end()) + m_pImpl->m_Streams.erase(it); +} + +//! Sets the flag to automatically flush buffers after each logged line +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::auto_flush(bool f) +{ + m_pImpl->m_fAutoFlush = f; +} + +//! The method writes the message to the sink +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::consume(record_view const&, string_type const& message) +{ + typename string_type::const_pointer const p = message.data(); + typename string_type::size_type const s = message.size(); + typename implementation::ostream_sequence::const_iterator + it = m_pImpl->m_Streams.begin(), end = m_pImpl->m_Streams.end(); + bool need_trailing_newline = false; + if (m_pImpl->m_AutoNewlineMode != disabled_auto_newline) + need_trailing_newline = (m_pImpl->m_AutoNewlineMode == always_insert || s == 0u || p[s - 1u] != static_cast< char_type >('\n')); + + for (; it != end; ++it) + { + stream_type* const strm = it->get(); + if (BOOST_LIKELY(strm->good())) + { + strm->write(p, static_cast< std::streamsize >(s)); + if (need_trailing_newline) + strm->put(static_cast< char_type >('\n')); + + if (m_pImpl->m_fAutoFlush) + strm->flush(); + } + } +} + +//! The method flushes the associated streams +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::flush() +{ + typename implementation::ostream_sequence::const_iterator + it = m_pImpl->m_Streams.begin(), end = m_pImpl->m_Streams.end(); + for (; it != end; ++it) + { + stream_type* const strm = it->get(); + if (BOOST_LIKELY(strm->good())) + strm->flush(); + } +} + +//! Explicitly instantiate sink backend implementation +#ifdef BOOST_LOG_USE_CHAR +template class basic_text_ostream_backend< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template class basic_text_ostream_backend< wchar_t >; +#endif + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/thread_id.cpp b/src/boost/libs/log/src/thread_id.cpp new file mode 100644 index 00000000..e3a70a46 --- /dev/null +++ b/src/boost/libs/log/src/thread_id.cpp @@ -0,0 +1,272 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 thread_id.cpp + * \author Andrey Semashev + * \date 08.1.2012 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> + +#if !defined(BOOST_LOG_NO_THREADS) + +#include <new> +#include <iostream> +#include <boost/throw_exception.hpp> +#if !defined(BOOST_WINDOWS) +#include <cstring> +#include <boost/predef/other/endian.h> +#endif +#include <boost/log/detail/thread_id.hpp> +#if defined(BOOST_LOG_USE_COMPILER_TLS) +#include <boost/aligned_storage.hpp> +#include <boost/type_traits/alignment_of.hpp> +#elif defined(BOOST_WINDOWS) +#include <boost/thread/thread.hpp> +#include <boost/log/detail/thread_specific.hpp> +#else +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/once_block.hpp> +#endif +#if !defined(BOOST_LOG_USE_COMPILER_TLS) +#include <boost/log/detail/singleton.hpp> +#endif +#include "id_formatting.hpp" +#include <boost/log/detail/header.hpp> + +#if defined(BOOST_WINDOWS) + +#include <windows.h> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +enum { tid_size = sizeof(GetCurrentThreadId()) }; + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + //! The function returns current process identifier + inline thread::id get_id_impl() + { + return thread::id(GetCurrentThreadId()); + } + +} // namespace + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#else // defined(BOOST_WINDOWS) + +#include <pthread.h> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +enum +{ + headroom_size = sizeof(pthread_t) > sizeof(uintmax_t) ? 0u : (sizeof(uintmax_t) - sizeof(pthread_t)), + tid_size = sizeof(uintmax_t) - headroom_size +}; + +BOOST_LOG_ANONYMOUS_NAMESPACE { + + //! The function returns current thread identifier + inline thread::id get_id_impl() + { + // According to POSIX, pthread_t may not be an integer type: + // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html + // For now we use the hackish cast to get some opaque number that hopefully correlates with system thread identification. + thread::id::native_type int_id = 0; + pthread_t pthread_id = pthread_self(); +#if BOOST_ENDIAN_BIG_BYTE || BOOST_ENDIAN_BIG_WORD + std::memcpy(reinterpret_cast< unsigned char* >(&int_id) + headroom_size, &pthread_id, tid_size); +#else + std::memcpy(&int_id, &pthread_id, tid_size); +#endif + return thread::id(int_id); + } + +} // namespace + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // defined(BOOST_WINDOWS) + + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +namespace this_thread { + +#if defined(BOOST_LOG_USE_COMPILER_TLS) + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +struct id_storage +{ + aligned_storage< sizeof(thread::id), alignment_of< thread::id >::value >::type m_storage; + bool m_initialized; +}; + +BOOST_LOG_TLS id_storage g_id_storage = {}; + +} // namespace + +//! The function returns current thread identifier +BOOST_LOG_API thread::id const& get_id() +{ + id_storage& s = g_id_storage; + if (BOOST_UNLIKELY(!s.m_initialized)) + { + new (s.m_storage.address()) thread::id(get_id_impl()); + s.m_initialized = true; + } + + return *static_cast< thread::id const* >(s.m_storage.address()); +} + +#elif defined(BOOST_WINDOWS) + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +struct id_storage : + lazy_singleton< id_storage > +{ + struct deleter + { + typedef void result_type; + + explicit deleter(thread::id const* p) : m_p(p) {} + + result_type operator() () const + { + delete m_p; + } + + private: + thread::id const* m_p; + }; + + thread_specific< thread::id const* > m_id; +}; + +} // namespace + +//! The function returns current thread identifier +BOOST_LOG_API thread::id const& get_id() +{ + id_storage& s = id_storage::get(); + thread::id const* p = s.m_id.get(); + if (BOOST_UNLIKELY(!p)) + { + p = new thread::id(get_id_impl()); + s.m_id.set(p); + boost::this_thread::at_thread_exit(id_storage::deleter(p)); + } + + return *p; +} + +#else + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +pthread_key_t g_key; + +void deleter(void* p) +{ + delete static_cast< thread::id* >(p); +} + +} // namespace + +//! The function returns current thread identifier +BOOST_LOG_API thread::id const& get_id() +{ + BOOST_LOG_ONCE_BLOCK() + { + if (int err = pthread_key_create(&g_key, &deleter)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to create a thread-specific storage for thread id", (err)); + } + } + + thread::id* p = static_cast< thread::id* >(pthread_getspecific(g_key)); + if (BOOST_UNLIKELY(!p)) + { + p = new thread::id(get_id_impl()); + pthread_setspecific(g_key, p); + } + + return *p; +} + +#endif + +} // namespace this_thread + +// Used in default_sink.cpp +void format_thread_id(char* buf, std::size_t size, thread::id tid) +{ + format_id< tid_size >(buf, size, tid.native_id(), false); +} + +template< typename CharT, typename TraitsT > +BOOST_LOG_API std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, thread::id const& tid) +{ + if (strm.good()) + { + CharT buf[tid_size * 2 + 3]; // 2 chars per byte + 3 chars for the leading 0x and terminating zero + format_id< tid_size >(buf, sizeof(buf) / sizeof(*buf), tid.native_id(), (strm.flags() & std::ios_base::uppercase) != 0); + + strm << buf; + } + + return strm; +} + +#if defined(BOOST_LOG_USE_CHAR) +template BOOST_LOG_API +std::basic_ostream< char, std::char_traits< char > >& +operator<< (std::basic_ostream< char, std::char_traits< char > >& strm, thread::id const& tid); +#endif // defined(BOOST_LOG_USE_CHAR) + +#if defined(BOOST_LOG_USE_WCHAR_T) +template BOOST_LOG_API +std::basic_ostream< wchar_t, std::char_traits< wchar_t > >& +operator<< (std::basic_ostream< wchar_t, std::char_traits< wchar_t > >& strm, thread::id const& tid); +#endif // defined(BOOST_LOG_USE_WCHAR_T) + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_NO_THREADS) diff --git a/src/boost/libs/log/src/thread_specific.cpp b/src/boost/libs/log/src/thread_specific.cpp new file mode 100644 index 00000000..d8255738 --- /dev/null +++ b/src/boost/libs/log/src/thread_specific.cpp @@ -0,0 +1,292 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 thread_specific.cpp + * \author Andrey Semashev + * \date 01.03.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <string> +#include <stdexcept> +#include <boost/log/exceptions.hpp> +#include <boost/log/detail/thread_specific.hpp> + +#if !defined(BOOST_LOG_NO_THREADS) + +#if defined(BOOST_THREAD_PLATFORM_WIN32) + +#include <windows.h> +#include <boost/system/error_code.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +thread_specific_base::thread_specific_base() +{ + m_Key = TlsAlloc(); + if (BOOST_UNLIKELY(m_Key == TLS_OUT_OF_INDEXES)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "TLS capacity depleted", (boost::system::errc::not_enough_memory)); + } +} + +thread_specific_base::~thread_specific_base() +{ + TlsFree(m_Key); +} + +void* thread_specific_base::get_content() const +{ + return TlsGetValue(m_Key); +} + +void thread_specific_base::set_content(void* value) const +{ + TlsSetValue(m_Key, value); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) + +#include <cstddef> +#include <cstring> +#include <pthread.h> +#include <boost/cstdint.hpp> +#include <boost/mpl/if.hpp> +#include <boost/type_traits/is_integral.hpp> +#include <boost/type_traits/is_signed.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! Some portability magic to detect how to store the TLS key +template< typename KeyT, bool IsStoreableV = sizeof(KeyT) <= sizeof(void*), bool IsIntegralV = boost::is_integral< KeyT >::value > +struct pthread_key_traits +{ + typedef KeyT pthread_key_type; + + static void allocate(void*& stg) + { + pthread_key_type* pkey = new pthread_key_type(); + const int res = pthread_key_create(pkey, NULL); + if (BOOST_UNLIKELY(res != 0)) + { + delete pkey; + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "TLS capacity depleted", (res)); + } + stg = pkey; + } + + static void deallocate(void* stg) + { + pthread_key_type* pkey = static_cast< pthread_key_type* >(stg); + pthread_key_delete(*pkey); + delete pkey; + } + + static void set_value(void* stg, void* value) + { + const int res = pthread_setspecific(*static_cast< pthread_key_type* >(stg), value); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to set TLS value", (res)); + } + } + + static void* get_value(void* stg) + { + return pthread_getspecific(*static_cast< pthread_key_type* >(stg)); + } +}; + +template< typename KeyT > +struct pthread_key_traits< KeyT, true, true > +{ + typedef KeyT pthread_key_type; + +#if defined(BOOST_HAS_INTPTR_T) + typedef typename mpl::if_c< + boost::is_signed< pthread_key_type >::value, + intptr_t, + uintptr_t + >::type intptr_type; +#else + typedef typename mpl::if_c< + boost::is_signed< pthread_key_type >::value, + std::ptrdiff_t, + std::size_t + >::type intptr_type; +#endif + + static void allocate(void*& stg) + { + pthread_key_type key; + const int res = pthread_key_create(&key, NULL); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "TLS capacity depleted", (res)); + } + stg = (void*)(intptr_type)key; + } + + static void deallocate(void* stg) + { + pthread_key_delete((pthread_key_type)(intptr_type)stg); + } + + static void set_value(void* stg, void* value) + { + const int res = pthread_setspecific((pthread_key_type)(intptr_type)stg, value); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to set TLS value", (res)); + } + } + + static void* get_value(void* stg) + { + return pthread_getspecific((pthread_key_type)(intptr_type)stg); + } +}; + +template< typename KeyT > +struct pthread_key_traits< KeyT, true, false > +{ + typedef KeyT pthread_key_type; + + static void allocate(void*& stg) + { + pthread_key_type key; + const int res = pthread_key_create(&key, NULL); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "TLS capacity depleted", (res)); + } + std::memset(&stg, 0, sizeof(stg)); + std::memcpy(&stg, &key, sizeof(pthread_key_type)); + } + + static void deallocate(void* stg) + { + pthread_key_type key; + std::memcpy(&key, &stg, sizeof(pthread_key_type)); + pthread_key_delete(key); + } + + static void set_value(void* stg, void* value) + { + pthread_key_type key; + std::memcpy(&key, &stg, sizeof(pthread_key_type)); + const int res = pthread_setspecific(key, value); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to set TLS value", (res)); + } + } + + static void* get_value(void* stg) + { + pthread_key_type key; + std::memcpy(&key, &stg, sizeof(pthread_key_type)); + return pthread_getspecific(key); + } +}; + +template< typename KeyT > +struct pthread_key_traits< KeyT*, true, false > +{ + typedef KeyT* pthread_key_type; + + static void allocate(void*& stg) + { + pthread_key_type key = NULL; + const int res = pthread_key_create(&key, NULL); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "TLS capacity depleted", (res)); + } + stg = static_cast< void* >(key); + } + + static void deallocate(void* stg) + { + pthread_key_delete(static_cast< pthread_key_type >(stg)); + } + + static void set_value(void* stg, void* value) + { + const int res = pthread_setspecific(static_cast< pthread_key_type >(stg), value); + if (BOOST_UNLIKELY(res != 0)) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to set TLS value", (res)); + } + } + + static void* get_value(void* stg) + { + return pthread_getspecific(static_cast< pthread_key_type >(stg)); + } +}; + +} // namespace + +thread_specific_base::thread_specific_base() +{ + typedef pthread_key_traits< pthread_key_t > traits_t; + traits_t::allocate(m_Key); +} + +thread_specific_base::~thread_specific_base() +{ + typedef pthread_key_traits< pthread_key_t > traits_t; + traits_t::deallocate(m_Key); +} + +void* thread_specific_base::get_content() const +{ + typedef pthread_key_traits< pthread_key_t > traits_t; + return traits_t::get_value(m_Key); +} + +void thread_specific_base::set_content(void* value) const +{ + typedef pthread_key_traits< pthread_key_t > traits_t; + traits_t::set_value(m_Key, value); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#else +#error Boost.Log: unsupported threading API +#endif + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_NO_THREADS) diff --git a/src/boost/libs/log/src/threadsafe_queue.cpp b/src/boost/libs/log/src/threadsafe_queue.cpp new file mode 100644 index 00000000..bbad463d --- /dev/null +++ b/src/boost/libs/log/src/threadsafe_queue.cpp @@ -0,0 +1,160 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 threadsafe_queue.cpp + * \author Andrey Semashev + * \date 05.11.2010 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * The implementation is based on algorithms published in the "Simple, Fast, + * and Practical Non-Blocking and Blocking Concurrent Queue Algorithms" article + * in PODC96 by Maged M. Michael and Michael L. Scott. Pseudocode is available here: + * http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html + * + * The lock-free version of the mentioned algorithms contain a race condition and therefore + * were not included here. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/log/detail/threadsafe_queue.hpp> + +#ifndef BOOST_LOG_NO_THREADS + +#include <new> +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +#include <boost/align/aligned_alloc.hpp> +#include <boost/type_traits/alignment_of.hpp> +#include <boost/log/detail/adaptive_mutex.hpp> +#include <boost/log/detail/locks.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Generic queue implementation with two locks +class threadsafe_queue_impl_generic : + public threadsafe_queue_impl +{ +private: + //! Mutex type to be used + typedef adaptive_mutex mutex_type; + + /*! + * A structure that contains a pointer to the node and the associated mutex. + * The alignment below allows to eliminate false sharing, it should not be less than CPU cache line size. + */ + struct BOOST_ALIGNMENT(BOOST_LOG_CPU_CACHE_LINE_SIZE) pointer + { + //! Pointer to the either end of the queue + node_base* node; + //! Lock for access synchronization + mutex_type mutex; + // 128 bytes padding is chosen to mitigate false sharing for NetBurst CPUs, which load two cache lines in one go. + unsigned char padding[128U - (sizeof(node_base*) + sizeof(mutex_type)) % 128U]; + }; + +private: + //! Pointer to the beginning of the queue + pointer m_Head; + //! Pointer to the end of the queue + pointer m_Tail; + +public: + explicit threadsafe_queue_impl_generic(node_base* first_node) + { + set_next(first_node, NULL); + m_Head.node = m_Tail.node = first_node; + } + + ~threadsafe_queue_impl_generic() + { + } + + node_base* reset_last_node() + { + BOOST_ASSERT(m_Head.node == m_Tail.node); + node_base* p = m_Head.node; + m_Head.node = m_Tail.node = NULL; + return p; + } + + bool unsafe_empty() + { + return m_Head.node == m_Tail.node; + } + + void push(node_base* p) + { + set_next(p, NULL); + exclusive_lock_guard< mutex_type > _(m_Tail.mutex); + set_next(m_Tail.node, p); + m_Tail.node = p; + } + + bool try_pop(node_base*& node_to_free, node_base*& node_with_value) + { + exclusive_lock_guard< mutex_type > _(m_Head.mutex); + node_base* next = get_next(m_Head.node); + if (next) + { + // We have a node to pop + node_to_free = m_Head.node; + node_with_value = m_Head.node = next; + return true; + } + else + return false; + } + +private: + BOOST_FORCEINLINE static void set_next(node_base* p, node_base* next) + { + p->next.data[0] = next; + } + BOOST_FORCEINLINE static node_base* get_next(node_base* p) + { + return static_cast< node_base* >(p->next.data[0]); + } + + // Copying and assignment are closed + BOOST_DELETED_FUNCTION(threadsafe_queue_impl_generic(threadsafe_queue_impl_generic const&)) + BOOST_DELETED_FUNCTION(threadsafe_queue_impl_generic& operator= (threadsafe_queue_impl_generic const&)) +}; + +BOOST_LOG_API threadsafe_queue_impl* threadsafe_queue_impl::create(node_base* first_node) +{ + return new threadsafe_queue_impl_generic(first_node); +} + +BOOST_LOG_API void* threadsafe_queue_impl::operator new (std::size_t size) +{ + void* p = alignment::aligned_alloc(BOOST_LOG_CPU_CACHE_LINE_SIZE, size); + if (BOOST_UNLIKELY(!p)) + BOOST_THROW_EXCEPTION(std::bad_alloc()); + return p; +} + +BOOST_LOG_API void threadsafe_queue_impl::operator delete (void* p, std::size_t) +{ + alignment::aligned_free(p); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_NO_THREADS diff --git a/src/boost/libs/log/src/timer.cpp b/src/boost/libs/log/src/timer.cpp new file mode 100644 index 00000000..f9b4cdfd --- /dev/null +++ b/src/boost/libs/log/src/timer.cpp @@ -0,0 +1,159 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 timer.cpp + * \author Andrey Semashev + * \date 02.12.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/log/attributes/timer.hpp> +#include <boost/log/attributes/attribute_value_impl.hpp> + +#if defined(BOOST_WINDOWS) && !defined(BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER) + +#include <boost/assert.hpp> +#include <boost/cstdint.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <boost/log/detail/locks.hpp> +#include <boost/thread/mutex.hpp> +#endif +#include <windows.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace attributes { + +//! Factory implementation +class BOOST_SYMBOL_VISIBLE timer::impl : + public attribute::impl +{ +private: +#if !defined(BOOST_LOG_NO_THREADS) + //! Synchronization mutex type + typedef boost::mutex mutex_type; + //! Synchronization mutex + mutex_type m_Mutex; +#endif + //! Frequency factor for calculating duration + double m_FrequencyFactor; + //! Last value of the performance counter + uint64_t m_LastCounter; + //! Elapsed time duration, in microseconds + uint64_t m_Duration; + +public: + //! Constructor + impl() : m_Duration(0) + { + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + BOOST_ASSERT(li.QuadPart != 0LL); + m_FrequencyFactor = 1000000.0 / static_cast< double >(li.QuadPart); + + QueryPerformanceCounter(&li); + m_LastCounter = static_cast< uint64_t >(li.QuadPart); + } + + //! The method returns the actual attribute value. It must not return NULL. + attribute_value get_value() + { + uint64_t duration; + { + BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);) + + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + const uint64_t counter = static_cast< uint64_t >(li.QuadPart); + const uint64_t counts = counter - m_LastCounter; + m_LastCounter = counter; + duration = m_Duration + static_cast< uint64_t >(counts * m_FrequencyFactor); + m_Duration = duration; + } + + return attribute_value(new attribute_value_impl< value_type >(boost::posix_time::microseconds(duration))); + } +}; + +//! Constructor +timer::timer() : attribute(new impl()) +{ +} + +//! Constructor for casting support +timer::timer(cast_source const& source) : attribute(source.as< impl >()) +{ +} + +} // namespace attributes + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#else // defined(BOOST_WINDOWS) && !defined(BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER) + +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace attributes { + +//! Factory implementation +class BOOST_SYMBOL_VISIBLE timer::impl : + public attribute::impl +{ +public: + //! Time type + typedef utc_time_traits::time_type time_type; + +private: + //! Base time point + const time_type m_BaseTimePoint; + +public: + /*! + * Constructor. Starts time counting. + */ + impl() : m_BaseTimePoint(utc_time_traits::get_clock()) {} + + attribute_value get_value() + { + return attribute_value(new attribute_value_impl< value_type >( + utc_time_traits::get_clock() - m_BaseTimePoint)); + } +}; + +//! Constructor +timer::timer() : attribute(new impl()) +{ +} + +//! Constructor for casting support +timer::timer(cast_source const& source) : attribute(source.as< impl >()) +{ +} + +} // namespace attributes + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // defined(BOOST_WINDOWS) && !defined(BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER) diff --git a/src/boost/libs/log/src/timestamp.cpp b/src/boost/libs/log/src/timestamp.cpp new file mode 100644 index 00000000..05e5e50c --- /dev/null +++ b/src/boost/libs/log/src/timestamp.cpp @@ -0,0 +1,279 @@ +/* + * Copyright Andrey Semashev 2007 - 2018. + * 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 timestamp.cpp + * \author Andrey Semashev + * \date 31.07.2011 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/log/detail/timestamp.hpp> + +#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#include <cstddef> +#include <cstdlib> +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/winapi/dll.hpp> +#include <boost/winapi/time.hpp> +#include <boost/winapi/event.hpp> +#include <boost/winapi/handles.hpp> +#include <boost/winapi/thread_pool.hpp> +#else +#include <unistd.h> // for config macros +#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +#include <mach/mach_time.h> +#include <mach/kern_return.h> +#include <boost/log/utility/once_block.hpp> +#include <boost/system/error_code.hpp> +#endif +#include <time.h> +#include <errno.h> +#include <boost/throw_exception.hpp> +#include <boost/log/exceptions.hpp> +#endif +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +// Directly use API from Vista and later +BOOST_LOG_API get_tick_count_t get_tick_count = &boost::winapi::GetTickCount64; + +#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +enum init_state +{ + uninitialized = 0, + in_progress, + initialized +}; + +struct get_tick_count64_state +{ + boost::atomic< uint64_t > ticks; + boost::atomic< init_state > init; + boost::winapi::HANDLE_ wait_event; + boost::winapi::HANDLE_ wait_handle; +}; + +// Zero-initialized initially +BOOST_ALIGNMENT(BOOST_LOG_CPU_CACHE_LINE_SIZE) static get_tick_count64_state g_state; + +//! Artifical implementation of GetTickCount64 +uint64_t BOOST_WINAPI_WINAPI_CC get_tick_count64() +{ + // Note: Even in single-threaded builds we have to implement get_tick_count64 in a thread-safe way because + // it can be called in the system thread pool during refreshes concurrently with user's calls. + uint64_t old_state = g_state.ticks.load(boost::memory_order_acquire); + + uint32_t new_ticks = boost::winapi::GetTickCount(); + + uint32_t old_ticks = static_cast< uint32_t >(old_state & UINT64_C(0x00000000ffffffff)); + uint64_t new_state = ((old_state & UINT64_C(0xffffffff00000000)) + (static_cast< uint64_t >(new_ticks < old_ticks) << 32)) | static_cast< uint64_t >(new_ticks); + + g_state.ticks.store(new_state, boost::memory_order_release); + + return new_state; +} + +//! The function is called periodically in the system thread pool to make sure g_state.ticks is timely updated +void BOOST_WINAPI_NTAPI_CC refresh_get_tick_count64(boost::winapi::PVOID_, boost::winapi::BOOLEAN_) +{ + get_tick_count64(); +} + +//! Cleanup function to stop get_tick_count64 refreshes +void cleanup_get_tick_count64() +{ + if (g_state.wait_handle) + { + boost::winapi::UnregisterWait(g_state.wait_handle); + g_state.wait_handle = NULL; + } + + if (g_state.wait_event) + { + boost::winapi::CloseHandle(g_state.wait_event); + g_state.wait_event = NULL; + } +} + +uint64_t BOOST_WINAPI_WINAPI_CC get_tick_count_init() +{ + boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); + if (hKernel32) + { + get_tick_count_t p = (get_tick_count_t)boost::winapi::get_proc_address(hKernel32, "GetTickCount64"); + if (p) + { + // Use native API + const_cast< get_tick_count_t volatile& >(get_tick_count) = p; + return p(); + } + } + + // No native API available. Use emulation with periodic refreshes to make sure the GetTickCount wrap arounds are properly counted. + init_state old_init = uninitialized; + if (g_state.init.compare_exchange_strong(old_init, in_progress, boost::memory_order_acq_rel, boost::memory_order_relaxed)) + { + if (!g_state.wait_event) + g_state.wait_event = boost::winapi::create_anonymous_event(NULL, false, false); + if (g_state.wait_event) + { + boost::winapi::BOOL_ res = boost::winapi::RegisterWaitForSingleObject(&g_state.wait_handle, g_state.wait_event, &refresh_get_tick_count64, NULL, 0x7fffffff, boost::winapi::WT_EXECUTEINWAITTHREAD_); + if (res) + { + std::atexit(&cleanup_get_tick_count64); + + const_cast< get_tick_count_t volatile& >(get_tick_count) = &get_tick_count64; + g_state.init.store(initialized, boost::memory_order_release); + goto finish; + } + } + + g_state.init.store(uninitialized, boost::memory_order_release); + } + +finish: + return get_tick_count64(); +} + +} // namespace + +BOOST_LOG_API get_tick_count_t get_tick_count = &get_tick_count_init; + +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +#elif (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0) > 0) /* POSIX timers supported */ \ + || defined(__GNU__) || defined(__OpenBSD__) || defined(__CloudABI__) /* GNU Hurd, OpenBSD and Nuxi CloudABI don't support POSIX timers fully but do provide clock_gettime() */ + +BOOST_LOG_API int64_t duration::milliseconds() const +{ + // Timestamps are always in nanoseconds + return m_ticks / INT64_C(1000000); +} + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +/*! + * \c get_timestamp implementation based on POSIX realtime clock. + * Note that this implementation is only used as a last resort since + * this timer can be manually set and may jump due to DST change. + */ +timestamp get_timestamp_realtime_clock() +{ + timespec ts; + if (BOOST_UNLIKELY(clock_gettime(CLOCK_REALTIME, &ts) != 0)) + { + const int err = errno; + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to acquire current time", (err)); + } + + return timestamp(static_cast< uint64_t >(ts.tv_sec) * UINT64_C(1000000000) + ts.tv_nsec); +} + +# if defined(_POSIX_MONOTONIC_CLOCK) + +//! \c get_timestamp implementation based on POSIX monotonic clock +timestamp get_timestamp_monotonic_clock() +{ + timespec ts; + if (BOOST_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) + { + const int err = errno; + if (err == EINVAL) + { + // The current platform does not support monotonic timer. + // Fall back to realtime clock, which is not exactly what we need + // but is better than nothing. + get_timestamp = &get_timestamp_realtime_clock; + return get_timestamp_realtime_clock(); + } + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to acquire current time", (err)); + } + + return timestamp(static_cast< uint64_t >(ts.tv_sec) * UINT64_C(1000000000) + ts.tv_nsec); +} + +# define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_monotonic_clock + +# else // if defined(_POSIX_MONOTONIC_CLOCK) +# define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_realtime_clock +# endif // if defined(_POSIX_MONOTONIC_CLOCK) + +} // namespace + +// Use POSIX API +BOOST_LOG_API get_timestamp_t get_timestamp = &BOOST_LOG_DEFAULT_GET_TIMESTAMP; + +# undef BOOST_LOG_DEFAULT_GET_TIMESTAMP + +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + +BOOST_LOG_API int64_t duration::milliseconds() const +{ + static mach_timebase_info_data_t timebase_info = {}; + BOOST_LOG_ONCE_BLOCK() + { + kern_return_t err = mach_timebase_info(&timebase_info); + if (err != KERN_SUCCESS) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to initialize timebase info", (boost::system::errc::not_supported)); + } + } + + // Often the timebase rational equals 1, we can optimize for this case + if (timebase_info.numer == timebase_info.denom) + { + // Timestamps are in nanoseconds + return m_ticks / INT64_C(1000000); + } + else + { + return (m_ticks * timebase_info.numer) / (INT64_C(1000000) * timebase_info.denom); + } +} + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +//! \c get_timestamp implementation based on MacOS X absolute time +timestamp get_timestamp_mach() +{ + return timestamp(mach_absolute_time()); +} + +} // namespace + +// Use MacOS X API +BOOST_LOG_API get_timestamp_t get_timestamp = &get_timestamp_mach; + +#else + +# error Boost.Log: Timestamp generation is not supported for your platform + +#endif + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/trivial.cpp b/src/boost/libs/log/src/trivial.cpp new file mode 100644 index 00000000..1461ad71 --- /dev/null +++ b/src/boost/libs/log/src/trivial.cpp @@ -0,0 +1,155 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 trivial.cpp + * \author Andrey Semashev + * \date 07.11.2009 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> + +#if defined(BOOST_LOG_USE_CHAR) + +#include <string> +#include <istream> +#include <boost/log/trivial.hpp> +#include <boost/log/sources/global_logger_storage.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace trivial { + +//! Initialization routine +BOOST_LOG_API logger::logger_type logger::construct_logger() +{ + return logger_type(keywords::severity = info); +} + +//! Returns a reference to the trivial logger instance +BOOST_LOG_API logger::logger_type& logger::get() +{ + return log::sources::aux::logger_singleton< logger >::get(); +} + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +BOOST_CONSTEXPR_OR_CONST unsigned int names_count = 6; + +template< typename CharT > +struct severity_level_names +{ + static const CharT names[names_count][8]; +}; + +template< typename CharT > +const CharT severity_level_names< CharT >::names[names_count][8] = +{ + { 't', 'r', 'a', 'c', 'e', 0 }, + { 'd', 'e', 'b', 'u', 'g', 0 }, + { 'i', 'n', 'f', 'o', 0 }, + { 'w', 'a', 'r', 'n', 'i', 'n', 'g', 0 }, + { 'e', 'r', 'r', 'o', 'r', 0 }, + { 'f', 'a', 't', 'a', 'l', 0 } +}; + +} // namespace + +template< typename CharT > +BOOST_LOG_API const CharT* to_string(severity_level lvl) +{ + typedef severity_level_names< CharT > level_names; + if (BOOST_LIKELY(static_cast< unsigned int >(lvl) < names_count)) + return level_names::names[static_cast< unsigned int >(lvl)]; + return NULL; +} + +//! Parses enumeration value from string and returns \c true on success and \c false otherwise +template< typename CharT > +BOOST_LOG_API bool from_string(const CharT* str, std::size_t len, severity_level& lvl) +{ + typedef severity_level_names< CharT > level_names; + typedef std::char_traits< CharT > char_traits; + + if (len == 5u) + { + if (char_traits::compare(str, level_names::names[0], len) == 0) + lvl = static_cast< severity_level >(0); + else if (char_traits::compare(str, level_names::names[1], len) == 0) + lvl = static_cast< severity_level >(1); + else if (char_traits::compare(str, level_names::names[4], len) == 0) + lvl = static_cast< severity_level >(4); + else if (char_traits::compare(str, level_names::names[5], len) == 0) + lvl = static_cast< severity_level >(5); + else + goto no_match; + return true; + } + else if (len == 4u) + { + if (char_traits::compare(str, level_names::names[2], len) == 0) + lvl = static_cast< severity_level >(2); + else + goto no_match; + return true; + } + else if (len == 7u) + { + if (char_traits::compare(str, level_names::names[3], len) == 0) + lvl = static_cast< severity_level >(3); + else + goto no_match; + return true; + } + +no_match: + return false; +} + +template< typename CharT, typename TraitsT > +BOOST_LOG_API std::basic_istream< CharT, TraitsT >& operator>> ( + std::basic_istream< CharT, TraitsT >& strm, severity_level& lvl) +{ + if (BOOST_LIKELY(strm.good())) + { + typedef std::basic_string< CharT, TraitsT > string_type; + string_type str; + strm >> str; + if (BOOST_UNLIKELY(!boost::log::trivial::from_string(str.data(), str.size(), lvl))) + strm.setstate(std::ios_base::failbit); + } + + return strm; +} + +template BOOST_LOG_API const char* to_string< char >(severity_level lvl); +template BOOST_LOG_API bool from_string< char >(const char* begin, std::size_t len, severity_level& lvl); +template BOOST_LOG_API std::basic_istream< char, std::char_traits< char > >& + operator>> < char, std::char_traits< char > > ( + std::basic_istream< char, std::char_traits< char > >& strm, severity_level& lvl); +#ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_API const wchar_t* to_string< wchar_t >(severity_level lvl); +template BOOST_LOG_API bool from_string< wchar_t >(const wchar_t* begin, std::size_t len, severity_level& lvl); +template BOOST_LOG_API std::basic_istream< wchar_t, std::char_traits< wchar_t > >& + operator>> < wchar_t, std::char_traits< wchar_t > > ( + std::basic_istream< wchar_t, std::char_traits< wchar_t > >& strm, severity_level& lvl); +#endif + +} // namespace trivial + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // defined(BOOST_LOG_USE_CHAR) diff --git a/src/boost/libs/log/src/unique_ptr.hpp b/src/boost/libs/log/src/unique_ptr.hpp new file mode 100644 index 00000000..091bb9cf --- /dev/null +++ b/src/boost/libs/log/src/unique_ptr.hpp @@ -0,0 +1,59 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 unique_ptr.hpp + * \author Andrey Semashev + * \date 18.07.2015 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_UNIQUE_PTR_HPP_INCLUDED_ +#define BOOST_LOG_UNIQUE_PTR_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> + +#if !defined(BOOST_NO_CXX11_SMART_PTR) + +#include <memory> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +using std::unique_ptr; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#else // !defined(BOOST_NO_CXX11_SMART_PTR) + +#include <boost/move/unique_ptr.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +using boost::movelib::unique_ptr; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // !defined(BOOST_NO_CXX11_SMART_PTR) + +#endif // BOOST_LOG_UNIQUE_PTR_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/windows/auto_handle.hpp b/src/boost/libs/log/src/windows/auto_handle.hpp new file mode 100644 index 00000000..ebd01bab --- /dev/null +++ b/src/boost/libs/log/src/windows/auto_handle.hpp @@ -0,0 +1,79 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/auto_handle.hpp + * \author Andrey Semashev + * \date 07.03.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WINDOWS_AUTO_HANDLE_HPP_INCLUDED_ +#define BOOST_LOG_WINDOWS_AUTO_HANDLE_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/assert.hpp> +#include <boost/winapi/handles.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +namespace aux { + +//! A wrapper around a kernel object handle. Automatically closes the handle on destruction. +class auto_handle +{ +private: + boost::winapi::HANDLE_ m_handle; + +public: + explicit auto_handle(boost::winapi::HANDLE_ h = NULL) BOOST_NOEXCEPT : m_handle(h) + { + } + + ~auto_handle() BOOST_NOEXCEPT + { + if (m_handle) + BOOST_VERIFY(boost::winapi::CloseHandle(m_handle) != 0); + } + + void init(boost::winapi::HANDLE_ h) BOOST_NOEXCEPT + { + BOOST_ASSERT(m_handle == NULL); + m_handle = h; + } + + boost::winapi::HANDLE_ get() const BOOST_NOEXCEPT { return m_handle; } + boost::winapi::HANDLE_* get_ptr() BOOST_NOEXCEPT { return &m_handle; } + + void swap(auto_handle& that) BOOST_NOEXCEPT + { + boost::winapi::HANDLE_ h = m_handle; + m_handle = that.m_handle; + that.m_handle = h; + } + + BOOST_DELETED_FUNCTION(auto_handle(auto_handle const&)) + BOOST_DELETED_FUNCTION(auto_handle& operator=(auto_handle const&)) +}; + +} // namespace aux + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WINDOWS_AUTO_HANDLE_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/windows/debug_output_backend.cpp b/src/boost/libs/log/src/windows/debug_output_backend.cpp new file mode 100644 index 00000000..105dd68f --- /dev/null +++ b/src/boost/libs/log/src/windows/debug_output_backend.cpp @@ -0,0 +1,78 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 debug_output_backend.cpp + * \author Andrey Semashev + * \date 08.11.2008 + * + * \brief A logging sink backend that uses debugger output + */ + +#ifndef BOOST_LOG_WITHOUT_DEBUG_OUTPUT + +#include <boost/log/detail/config.hpp> +#include <string> +#include <boost/log/sinks/debug_output_backend.hpp> +#include <windows.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +#if defined(BOOST_LOG_USE_CHAR) + inline void output_debug_string(const char* str) + { + OutputDebugStringA(str); + } +#endif // defined(BOOST_LOG_USE_CHAR) +#if defined(BOOST_LOG_USE_WCHAR_T) + inline void output_debug_string(const wchar_t* str) + { + OutputDebugStringW(str); + } +#endif // defined(BOOST_LOG_USE_WCHAR_T) + +} // namespace + +template< typename CharT > +BOOST_LOG_API basic_debug_output_backend< CharT >::basic_debug_output_backend() +{ +} + +template< typename CharT > +BOOST_LOG_API basic_debug_output_backend< CharT >::~basic_debug_output_backend() +{ +} + +//! The method puts the formatted message to the event log +template< typename CharT > +BOOST_LOG_API void basic_debug_output_backend< CharT >::consume(record_view const&, string_type const& formatted_message) +{ + output_debug_string(formatted_message.c_str()); +} + +#ifdef BOOST_LOG_USE_CHAR +template class basic_debug_output_backend< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template class basic_debug_output_backend< wchar_t >; +#endif + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_WITHOUT_DEBUG_OUTPUT) diff --git a/src/boost/libs/log/src/windows/event_log_backend.cpp b/src/boost/libs/log/src/windows/event_log_backend.cpp new file mode 100644 index 00000000..a3382c8c --- /dev/null +++ b/src/boost/libs/log/src/windows/event_log_backend.cpp @@ -0,0 +1,635 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 event_log_backend.cpp + * \author Andrey Semashev + * \date 07.11.2008 + * + * \brief A logging sink backend that uses Windows NT event log API + * for signalling application events. + */ + +#ifndef BOOST_LOG_WITHOUT_EVENT_LOG + +#include <boost/log/detail/config.hpp> +#include <string> +#include <vector> +#include <ostream> +#include <stdexcept> +#include <boost/scoped_array.hpp> +#include <boost/system/windows_error.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/sinks/event_log_backend.hpp> +#include <boost/log/sinks/event_log_constants.hpp> +#include <boost/log/utility/once_block.hpp> +#include <boost/log/detail/cleanup_scope_guard.hpp> +#include <boost/log/detail/attachable_sstream_buf.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <windows.h> +#include <psapi.h> +#include "unique_ptr.hpp" +#include "windows/event_log_registry.hpp" +#include "windows/simple_event_log.h" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +namespace event_log { + + //! The function constructs log record level from an integer + BOOST_LOG_API event_type make_event_type(unsigned short lev) + { + switch (lev) + { + case success: return success; + case warning: return warning; + case error: return error; + default: + BOOST_THROW_EXCEPTION(std::out_of_range("Windows NT event type is out of range")); + BOOST_LOG_UNREACHABLE_RETURN(info); + case info: return info; + } + } + +} // namespace event_log + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +#ifdef BOOST_LOG_USE_CHAR + //! A simple forwarder to the ReportEvent API + inline BOOL report_event( + HANDLE hEventLog, + WORD wType, + WORD wCategory, + DWORD dwEventID, + PSID lpUserSid, + WORD wNumStrings, + DWORD dwDataSize, + const char** lpStrings, + LPVOID lpRawData) + { + return ReportEventA(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData); + } + //! A simple forwarder to the GetModuleFileName API + inline DWORD get_module_file_name(HMODULE hModule, char* lpFilename, DWORD nSize) + { + return GetModuleFileNameA(hModule, lpFilename, nSize); + } + //! A simple forwarder to the RegisterEventSource API + inline HANDLE register_event_source(const char* lpUNCServerName, const char* lpSourceName) + { + return RegisterEventSourceA(lpUNCServerName, lpSourceName); + } + //! The function completes default source name for the sink backend + inline void complete_default_simple_event_log_source_name(std::string& name) + { + name += " simple event source"; + } + //! The function completes default source name for the sink backend + inline void complete_default_event_log_source_name(std::string& name) + { + name += " event source"; + } +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + //! A simple forwarder to the ReportEvent API + inline BOOL report_event( + HANDLE hEventLog, + WORD wType, + WORD wCategory, + DWORD dwEventID, + PSID lpUserSid, + WORD wNumStrings, + DWORD dwDataSize, + const wchar_t** lpStrings, + LPVOID lpRawData) + { + return ReportEventW(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData); + } + //! A simple forwarder to the GetModuleFileName API + inline DWORD get_module_file_name(HMODULE hModule, wchar_t* lpFilename, DWORD nSize) + { + return GetModuleFileNameW(hModule, lpFilename, nSize); + } + //! A simple forwarder to the RegisterEventSource API + inline HANDLE register_event_source(const wchar_t* lpUNCServerName, const wchar_t* lpSourceName) + { + return RegisterEventSourceW(lpUNCServerName, lpSourceName); + } + //! The function completes default source name for the sink backend + inline void complete_default_simple_event_log_source_name(std::wstring& name) + { + name += L" simple event source"; + } + //! The function completes default source name for the sink backend + inline void complete_default_event_log_source_name(std::wstring& name) + { + name += L" event source"; + } +#endif // BOOST_LOG_USE_WCHAR_T + + //! The function finds the handle for the current module + void init_self_module_handle(HMODULE& handle) + { + // Acquire all modules of the current process + HANDLE hProcess = GetCurrentProcess(); + std::vector< HMODULE > modules; + DWORD module_count = 1024; + do + { + modules.resize(module_count, HMODULE(0)); + BOOL res = EnumProcessModules( + hProcess, + &modules[0], + static_cast< DWORD >(modules.size() * sizeof(HMODULE)), + &module_count); + module_count /= sizeof(HMODULE); + + if (!res) + { + DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not enumerate process modules", (err)); + } + } + while (module_count > modules.size()); + modules.resize(module_count, HMODULE(0)); + + // Now find the current module among them + void* p = (void*)&init_self_module_handle; + for (std::size_t i = 0, n = modules.size(); i < n; ++i) + { + MODULEINFO info; + if (!GetModuleInformation(hProcess, modules[i], &info, sizeof(info))) + { + DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not acquire module information", (err)); + } + + if (info.lpBaseOfDll <= p && (static_cast< unsigned char* >(info.lpBaseOfDll) + info.SizeOfImage) > p) + { + // Found it + handle = modules[i]; + break; + } + } + + if (!handle) + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not find self module information", (boost::system::windows_error::invalid_handle)); + } + + //! Retrieves the full name of the current module (be that dll or exe) + template< typename CharT > + std::basic_string< CharT > get_current_module_name() + { + static HMODULE hSelfModule = 0; + + BOOST_LOG_ONCE_BLOCK() + { + init_self_module_handle(hSelfModule); + } + + // Get the module file name + CharT buf[MAX_PATH]; + DWORD size = get_module_file_name(hSelfModule, buf, sizeof(buf) / sizeof(*buf)); + if (size == 0) + { + DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not get module file name", (err)); + } + + return std::basic_string< CharT >(buf, buf + size); + } + +} // namespace + +////////////////////////////////////////////////////////////////////////// +// Simple event log backend implementation +////////////////////////////////////////////////////////////////////////// +//! Sink backend implementation +template< typename CharT > +struct basic_simple_event_log_backend< CharT >::implementation +{ + //! A handle for the registered event provider + HANDLE m_SourceHandle; + //! A level mapping functor + event_type_mapper_type m_LevelMapper; + + implementation() : m_SourceHandle(0) + { + } +}; + +//! Default constructor. Registers event source Boost.Log <Boost version> in the Application log. +template< typename CharT > +BOOST_LOG_API basic_simple_event_log_backend< CharT >::basic_simple_event_log_backend() +{ + construct(log::aux::empty_arg_list()); +} + +//! Destructor +template< typename CharT > +BOOST_LOG_API basic_simple_event_log_backend< CharT >::~basic_simple_event_log_backend() +{ + DeregisterEventSource(m_pImpl->m_SourceHandle); + delete m_pImpl; +} + +//! Constructs backend implementation +template< typename CharT > +BOOST_LOG_API void basic_simple_event_log_backend< CharT >::construct( + string_type const& target, string_type const& log_name, string_type const& source_name, event_log::registration_mode reg_mode) +{ + if (reg_mode != event_log::never) + { + aux::registry_params< char_type > reg_params; + reg_params.event_message_file = get_current_module_name< char_type >(); + reg_params.types_supported = DWORD( + EVENTLOG_SUCCESS | + EVENTLOG_INFORMATION_TYPE | + EVENTLOG_WARNING_TYPE | + EVENTLOG_ERROR_TYPE); + aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params); + } + + log::aux::unique_ptr< implementation > p(new implementation()); + + const char_type* target_unc = NULL; + if (!target.empty()) + target_unc = target.c_str(); + + HANDLE hSource = register_event_source(target_unc, source_name.c_str()); + if (!hSource) + { + const DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not register event source", (err)); + } + + p->m_SourceHandle = hSource; + + m_pImpl = p.release(); +} + +//! Returns default log name +template< typename CharT > +BOOST_LOG_API typename basic_simple_event_log_backend< CharT >::string_type +basic_simple_event_log_backend< CharT >::get_default_log_name() +{ + return aux::registry_traits< char_type >::make_default_log_name(); +} + +//! Returns default source name +template< typename CharT > +BOOST_LOG_API typename basic_simple_event_log_backend< CharT >::string_type +basic_simple_event_log_backend< CharT >::get_default_source_name() +{ + string_type source_name = aux::registry_traits< char_type >::make_default_source_name(); + complete_default_simple_event_log_source_name(source_name); + return source_name; +} + +//! The method installs the function object that maps application severity levels to WinAPI event types +template< typename CharT > +BOOST_LOG_API void basic_simple_event_log_backend< CharT >::set_event_type_mapper(event_type_mapper_type const& mapper) +{ + m_pImpl->m_LevelMapper = mapper; +} + +//! The method puts the formatted message to the event log +template< typename CharT > +BOOST_LOG_API void basic_simple_event_log_backend< CharT >::consume(record_view const& rec, string_type const& formatted_message) +{ + const char_type* message = formatted_message.c_str(); + event_log::event_type evt_type = event_log::info; + if (!m_pImpl->m_LevelMapper.empty()) + evt_type = m_pImpl->m_LevelMapper(rec); + + DWORD event_id; + switch (evt_type) + { + case event_log::success: + event_id = BOOST_LOG_MSG_DEBUG; break; + case event_log::warning: + event_id = BOOST_LOG_MSG_WARNING; break; + case event_log::error: + event_id = BOOST_LOG_MSG_ERROR; break; + default: + event_id = BOOST_LOG_MSG_INFO; break; + } + + report_event( + m_pImpl->m_SourceHandle, // Event log handle. + static_cast< WORD >(evt_type), // Event type. + 0, // Event category. + event_id, // Event identifier. + NULL, // No user security identifier. + 1, // Number of substitution strings. + 0, // No data. + &message, // Pointer to strings. + NULL); // No data. +} + +////////////////////////////////////////////////////////////////////////// +// Customizable event log backend implementation +////////////////////////////////////////////////////////////////////////// +namespace event_log { + + template< typename CharT > + class basic_event_composer< CharT >::insertion_composer + { + public: + //! Function object result type + typedef void result_type; + + private: + //! The list of insertion composers (in backward order) + typedef std::vector< formatter_type > formatters; + + private: + //! The insertion string composers + formatters m_Formatters; + + public: + //! Default constructor + insertion_composer() {} + //! Composition operator + void operator() (record_view const& rec, insertion_list& insertions) const + { + std::size_t size = m_Formatters.size(); + insertions.resize(size); + for (std::size_t i = 0; i < size; ++i) + { + typename formatter_type::stream_type strm(insertions[i]); + m_Formatters[i](rec, strm); + strm.flush(); + } + } + //! Adds a new formatter to the list + void add_formatter(formatter_type const& fmt) + { + m_Formatters.push_back(formatter_type(fmt)); + } + }; + + //! Default constructor + template< typename CharT > + basic_event_composer< CharT >::basic_event_composer(event_id_mapper_type const& id_mapper) : + m_EventIDMapper(id_mapper) + { + } + //! Copy constructor + template< typename CharT > + basic_event_composer< CharT >::basic_event_composer(basic_event_composer const& that) : + m_EventIDMapper(that.m_EventIDMapper), + m_EventMap(that.m_EventMap) + { + } + //! Destructor + template< typename CharT > + basic_event_composer< CharT >::~basic_event_composer() + { + } + + //! Assignment + template< typename CharT > + basic_event_composer< CharT >& basic_event_composer< CharT >::operator= (basic_event_composer that) + { + swap(that); + return *this; + } + //! Swapping + template< typename CharT > + void basic_event_composer< CharT >::swap(basic_event_composer& that) + { + m_EventIDMapper.swap(that.m_EventIDMapper); + m_EventMap.swap(that.m_EventMap); + } + //! Creates a new entry for a message + template< typename CharT > + typename basic_event_composer< CharT >::event_map_reference + basic_event_composer< CharT >::operator[] (event_id id) + { + return event_map_reference(id, *this); + } + //! Creates a new entry for a message + template< typename CharT > + typename basic_event_composer< CharT >::event_map_reference + basic_event_composer< CharT >::operator[] (int id) + { + return event_map_reference(make_event_id(id), *this); + } + + //! Event composition operator + template< typename CharT > + event_id basic_event_composer< CharT >::operator() (record_view const& rec, insertion_list& insertions) const + { + event_id id = m_EventIDMapper(rec); + typename event_map::const_iterator it = m_EventMap.find(id); + if (it != m_EventMap.end()) + it->second(rec, insertions); + return id; + } + + //! Adds a formatter to the insertion composers list + template< typename CharT > + typename basic_event_composer< CharT >::insertion_composer* + basic_event_composer< CharT >::add_formatter(event_id id, insertion_composer* composer, formatter_type const& fmt) + { + if (!composer) + composer = &m_EventMap[id]; + composer->add_formatter(fmt); + return composer; + } + +#ifdef BOOST_LOG_USE_CHAR + template class BOOST_LOG_API basic_event_composer< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T + template class BOOST_LOG_API basic_event_composer< wchar_t >; +#endif + +} // namespace event_log + + +//! Backend implementation +template< typename CharT > +struct basic_event_log_backend< CharT >::implementation +{ + // NOTE: This order of data members is critical for MSVC 9.0 in debug mode, + // as it ICEs if boost::functions are not the first members. Doh! + + //! An event category mapper + event_category_mapper_type m_CategoryMapper; + //! A level mapping functor + event_type_mapper_type m_LevelMapper; + + //! A handle for the registered event provider + HANDLE m_SourceHandle; + //! A functor that composes an event + event_composer_type m_EventComposer; + //! An array of formatted insertions + insertion_list m_Insertions; + + implementation() : m_SourceHandle(0) + { + } +}; + +//! Destructor +template< typename CharT > +BOOST_LOG_API basic_event_log_backend< CharT >::~basic_event_log_backend() +{ + DeregisterEventSource(m_pImpl->m_SourceHandle); + delete m_pImpl; +} + +//! Constructs backend implementation +template< typename CharT > +BOOST_LOG_API void basic_event_log_backend< CharT >::construct( + filesystem::path const& message_file_name, + string_type const& target, + string_type const& log_name, + string_type const& source_name, + event_log::registration_mode reg_mode) +{ + if (reg_mode != event_log::never) + { + if (message_file_name.empty()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Message file name not specified.")); + aux::registry_params< char_type > reg_params; + string_type file_name; + log::aux::code_convert(message_file_name.string(), file_name); + reg_params.event_message_file = file_name; + reg_params.types_supported = DWORD( + EVENTLOG_SUCCESS | + EVENTLOG_INFORMATION_TYPE | + EVENTLOG_WARNING_TYPE | + EVENTLOG_ERROR_TYPE); + aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params); + } + + log::aux::unique_ptr< implementation > p(new implementation()); + + const char_type* target_unc = NULL; + if (!target.empty()) + target_unc = target.c_str(); + + HANDLE hSource = register_event_source(target_unc, source_name.c_str()); + if (!hSource) + { + const DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not register event source", (err)); + } + + p->m_SourceHandle = hSource; + + m_pImpl = p.release(); +} + +//! The method puts the formatted message to the event log +template< typename CharT > +BOOST_LOG_API void basic_event_log_backend< CharT >::consume(record_view const& rec) +{ + if (!m_pImpl->m_EventComposer.empty()) + { + log::aux::cleanup_guard< insertion_list > cleaner(m_pImpl->m_Insertions); + + // Get event ID and construct insertions + DWORD id = m_pImpl->m_EventComposer(rec, m_pImpl->m_Insertions); + WORD string_count = static_cast< WORD >(m_pImpl->m_Insertions.size()); + scoped_array< const char_type* > strings(new const char_type*[string_count]); + for (WORD i = 0; i < string_count; ++i) + strings[i] = m_pImpl->m_Insertions[i].c_str(); + + // Get event type + WORD event_type = EVENTLOG_INFORMATION_TYPE; + if (!m_pImpl->m_LevelMapper.empty()) + event_type = static_cast< WORD >(m_pImpl->m_LevelMapper(rec)); + + WORD event_category = 0; + if (!m_pImpl->m_CategoryMapper.empty()) + event_category = static_cast< WORD >(m_pImpl->m_CategoryMapper(rec)); + + report_event( + m_pImpl->m_SourceHandle, // Event log handle. + event_type, // Event type. + event_category, // Event category. + id, // Event identifier. + NULL, // No user security identifier. + string_count, // Number of substitution strings. + 0, // No data. + strings.get(), // Pointer to strings. + NULL); // No data. + } +} + +//! Returns default log name +template< typename CharT > +BOOST_LOG_API typename basic_event_log_backend< CharT >::string_type +basic_event_log_backend< CharT >::get_default_log_name() +{ + return aux::registry_traits< char_type >::make_default_log_name(); +} + +//! Returns default source name +template< typename CharT > +BOOST_LOG_API typename basic_event_log_backend< CharT >::string_type +basic_event_log_backend< CharT >::get_default_source_name() +{ + string_type source_name = aux::registry_traits< char_type >::make_default_source_name(); + complete_default_event_log_source_name(source_name); + return source_name; +} + +//! The method installs the function object that maps application severity levels to WinAPI event types +template< typename CharT > +BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_type_mapper(event_type_mapper_type const& mapper) +{ + m_pImpl->m_LevelMapper = mapper; +} + +//! The method installs the function object that extracts event category from attribute values +template< typename CharT > +BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_category_mapper(event_category_mapper_type const& mapper) +{ + m_pImpl->m_CategoryMapper = mapper; +} + +/*! + * The method installs the function object that extracts event identifier from the attributes and creates + * insertion strings that will replace placeholders in the event message. + */ +template< typename CharT > +BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_composer(event_composer_type const& composer) +{ + m_pImpl->m_EventComposer = composer; +} + + +#ifdef BOOST_LOG_USE_CHAR +template class basic_simple_event_log_backend< char >; +template class basic_event_log_backend< char >; +#endif +#ifdef BOOST_LOG_USE_WCHAR_T +template class basic_simple_event_log_backend< wchar_t >; +template class basic_event_log_backend< wchar_t >; +#endif + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_WITHOUT_EVENT_LOG) diff --git a/src/boost/libs/log/src/windows/event_log_registry.hpp b/src/boost/libs/log/src/windows/event_log_registry.hpp new file mode 100644 index 00000000..9d1579ac --- /dev/null +++ b/src/boost/libs/log/src/windows/event_log_registry.hpp @@ -0,0 +1,491 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 event_log_registry.hpp + * \author Andrey Semashev + * \date 16.11.2008 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WINDOWS_EVENT_LOG_REGISTRY_HPP_INCLUDED_ +#define BOOST_LOG_WINDOWS_EVENT_LOG_REGISTRY_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <cwchar> +#include <cstring> +#include <string> +#include <sstream> +#include <stdexcept> +#include <boost/version.hpp> +#include <boost/optional/optional.hpp> +#include <boost/log/detail/code_conversion.hpp> +#include <boost/log/exceptions.hpp> +#include <windows.h> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +namespace aux { + +// MSVC versions up to 2008 (or their Platform SDKs, to be more precise) don't define LSTATUS. +// Perhaps, that is also the case for MinGW and Cygwin (untested). +typedef DWORD LSTATUS; + +// Max registry string size, in characters (for security reasons) +const DWORD max_string_size = 64u * 1024u; + +//! Helper traits to integrate with WinAPI +template< typename CharT > +struct registry_traits; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct registry_traits< char > +{ + static std::string make_event_log_key(std::string const& log_name, std::string const& source_name) + { + return "SYSTEM\\CurrentControlSet\\Services\\EventLog\\" + log_name + "\\" + source_name; + } + + static std::string make_default_log_name() + { + return "Application"; + } + + static std::string make_default_source_name() + { + char buf[MAX_PATH]; + DWORD size = GetModuleFileNameA(NULL, buf, sizeof(buf) / sizeof(*buf)); + + std::string source_name(buf, buf + size); + if (source_name.empty()) + { + // In case of error we provide artificial application name + std::ostringstream strm; + strm << "Boost.Log " + << static_cast< unsigned int >(BOOST_VERSION / 100000) + << "." + << static_cast< unsigned int >(BOOST_VERSION / 100 % 1000) + << "." + << static_cast< unsigned int >(BOOST_VERSION % 100); + source_name = strm.str(); + } + else + { + // Cut off the path and extension + std::size_t backslash_pos = source_name.rfind('\\'); + if (backslash_pos == std::string::npos || backslash_pos >= source_name.size() - 1) + backslash_pos = 0; + else + ++backslash_pos; + std::size_t dot_pos = source_name.rfind('.'); + if (dot_pos == std::string::npos || dot_pos < backslash_pos) + dot_pos = source_name.size(); + source_name = source_name.substr(backslash_pos, dot_pos - backslash_pos); + } + + return source_name; + } + + static LSTATUS create_key( + HKEY hKey, + const char* lpSubKey, + DWORD Reserved, + char* lpClass, + DWORD dwOptions, + REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + PHKEY phkResult, + LPDWORD lpdwDisposition) + { + return RegCreateKeyExA(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); + } + + static LSTATUS open_key( + HKEY hKey, + const char* lpSubKey, + DWORD dwOptions, + REGSAM samDesired, + PHKEY phkResult) + { + return RegOpenKeyExA(hKey, lpSubKey, dwOptions, samDesired, phkResult); + } + + static LSTATUS set_value( + HKEY hKey, + const char* lpValueName, + DWORD Reserved, + DWORD dwType, + const BYTE* lpData, + DWORD cbData) + { + return RegSetValueExA(hKey, lpValueName, Reserved, dwType, lpData, cbData); + } + + static LSTATUS get_value(HKEY hKey, const char* lpValueName, DWORD& value) + { + DWORD type = REG_NONE, size = sizeof(value); + LSTATUS res = RegQueryValueExA(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value), &size); + if (res == ERROR_SUCCESS && type != REG_DWORD && type != REG_BINARY) + res = ERROR_INVALID_DATA; + return res; + } + + static LSTATUS get_value(HKEY hKey, const char* lpValueName, std::string& value) + { + DWORD type = REG_NONE, size = 0; + LSTATUS res = RegQueryValueExA(hKey, lpValueName, NULL, &type, NULL, &size); + if (res == ERROR_SUCCESS && ((type != REG_EXPAND_SZ && type != REG_SZ) || size > max_string_size)) + return ERROR_INVALID_DATA; + if (size == 0) + return res; + + value.resize(size); + res = RegQueryValueExA(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value[0]), &size); + value.resize(std::strlen(value.c_str())); // remove extra terminating zero + + return res; + } + + static const char* get_event_message_file_param_name() { return "EventMessageFile"; } + static const char* get_category_message_file_param_name() { return "CategoryMessageFile"; } + static const char* get_category_count_param_name() { return "CategoryCount"; } + static const char* get_types_supported_param_name() { return "TypesSupported"; } +}; +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct registry_traits< wchar_t > +{ + static std::wstring make_event_log_key(std::wstring const& log_name, std::wstring const& source_name) + { + return L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" + log_name + L"\\" + source_name; + } + + static std::wstring make_default_log_name() + { + return L"Application"; + } + + static std::wstring make_default_source_name() + { + wchar_t buf[MAX_PATH]; + DWORD size = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(*buf)); + + std::wstring source_name(buf, buf + size); + if (source_name.empty()) + { + // In case of error we provide artificial application name + std::wostringstream strm; + strm << L"Boost.Log " + << static_cast< unsigned int >(BOOST_VERSION / 100000) + << L"." + << static_cast< unsigned int >(BOOST_VERSION / 100 % 1000) + << L"." + << static_cast< unsigned int >(BOOST_VERSION % 100); + source_name = strm.str(); + } + else + { + // Cut off the path and extension + std::size_t backslash_pos = source_name.rfind(L'\\'); + if (backslash_pos == std::wstring::npos || backslash_pos >= source_name.size() - 1) + backslash_pos = 0; + else + ++backslash_pos; + std::size_t dot_pos = source_name.rfind(L'.'); + if (dot_pos == std::wstring::npos || dot_pos < backslash_pos) + dot_pos = source_name.size(); + source_name = source_name.substr(backslash_pos, dot_pos - backslash_pos); + } + + return source_name; + } + + static LSTATUS create_key( + HKEY hKey, + const wchar_t* lpSubKey, + DWORD Reserved, + wchar_t* lpClass, + DWORD dwOptions, + REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + PHKEY phkResult, + LPDWORD lpdwDisposition) + { + return RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); + } + + static LSTATUS open_key( + HKEY hKey, + const wchar_t* lpSubKey, + DWORD dwOptions, + REGSAM samDesired, + PHKEY phkResult) + { + return RegOpenKeyExW(hKey, lpSubKey, dwOptions, samDesired, phkResult); + } + + static LSTATUS set_value( + HKEY hKey, + const wchar_t* lpValueName, + DWORD Reserved, + DWORD dwType, + const BYTE* lpData, + DWORD cbData) + { + return RegSetValueExW(hKey, lpValueName, Reserved, dwType, lpData, cbData); + } + + static LSTATUS get_value(HKEY hKey, const wchar_t* lpValueName, DWORD& value) + { + DWORD type = REG_NONE, size = sizeof(value); + LSTATUS res = RegQueryValueExW(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value), &size); + if (res == ERROR_SUCCESS && type != REG_DWORD && type != REG_BINARY) + res = ERROR_INVALID_DATA; + return res; + } + + static LSTATUS get_value(HKEY hKey, const wchar_t* lpValueName, std::wstring& value) + { + DWORD type = REG_NONE, size = 0; + LSTATUS res = RegQueryValueExW(hKey, lpValueName, NULL, &type, NULL, &size); + size /= sizeof(wchar_t); + if (res == ERROR_SUCCESS && ((type != REG_EXPAND_SZ && type != REG_SZ) || size > max_string_size)) + return ERROR_INVALID_DATA; + if (size == 0) + return res; + + value.resize(size); + res = RegQueryValueExW(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value[0]), &size); + value.resize(std::wcslen(value.c_str())); // remove extra terminating zero + + return res; + } + + static const wchar_t* get_event_message_file_param_name() { return L"EventMessageFile"; } + static const wchar_t* get_category_message_file_param_name() { return L"CategoryMessageFile"; } + static const wchar_t* get_category_count_param_name() { return L"CategoryCount"; } + static const wchar_t* get_types_supported_param_name() { return L"TypesSupported"; } + +}; +#endif // BOOST_LOG_USE_WCHAR_T + +//! The structure with parameters that have to be registered in the event log registry key +template< typename CharT > +struct registry_params +{ + typedef std::basic_string< CharT > string_type; + + optional< string_type > event_message_file; + optional< string_type > category_message_file; + optional< DWORD > category_count; + optional< DWORD > types_supported; +}; + +//! A simple guard that closes the registry key on destruction +struct auto_hkey_close +{ + explicit auto_hkey_close(HKEY hk) : hk_(hk) {} + ~auto_hkey_close() { RegCloseKey(hk_); } + +private: + HKEY hk_; +}; + +//! The function checks if the event log is already registered +template< typename CharT > +bool verify_event_log_registry(std::basic_string< CharT > const& reg_key, bool force, registry_params< CharT > const& params) +{ + typedef std::basic_string< CharT > string_type; + typedef registry_traits< CharT > registry; + + // Open the key + HKEY hkey = 0; + LSTATUS res = registry::open_key( + HKEY_LOCAL_MACHINE, + reg_key.c_str(), + REG_OPTION_NON_VOLATILE, + KEY_READ, + &hkey); + if (res != ERROR_SUCCESS) + return false; + + auto_hkey_close hkey_guard(hkey); + + if (force) + { + // Verify key values + if (!!params.event_message_file) + { + string_type module_name; + res = registry::get_value(hkey, registry::get_event_message_file_param_name(), module_name); + if (res != ERROR_SUCCESS || module_name != params.event_message_file.get()) + return false; + } + + if (!!params.category_message_file) + { + string_type module_name; + res = registry::get_value(hkey, registry::get_category_message_file_param_name(), module_name); + if (res != ERROR_SUCCESS || module_name != params.category_message_file.get()) + return false; + } + + if (!!params.category_count) + { + // Set number of categories + DWORD category_count = 0; + res = registry::get_value(hkey, registry::get_category_count_param_name(), category_count); + if (res != ERROR_SUCCESS || category_count != params.category_count.get()) + return false; + } + + if (!!params.types_supported) + { + // Set the supported event types + DWORD event_types = 0; + res = registry::get_value(hkey, registry::get_types_supported_param_name(), event_types); + if (res != ERROR_SUCCESS || event_types != params.types_supported.get()) + return false; + } + } + + return true; +} + +//! The function initializes the event log registry key +template< typename CharT > +void init_event_log_registry( + std::basic_string< CharT > const& log_name, + std::basic_string< CharT > const& source_name, + bool force, + registry_params< CharT > const& params) +{ + typedef std::basic_string< CharT > string_type; + typedef registry_traits< CharT > registry; + // Registry key name that contains log description + string_type reg_key = registry::make_event_log_key(log_name, source_name); + + // First check the registry keys and values in read-only mode. + // This allows to avoid UAC asking for elevated permissions to modify HKLM registry when no modification is actually needed. + if (verify_event_log_registry(reg_key, force, params)) + return; + + // Create or open the key + HKEY hkey = 0; + DWORD disposition = 0; + LSTATUS res = registry::create_key( + HKEY_LOCAL_MACHINE, + reg_key.c_str(), + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + NULL, + &hkey, + &disposition); + if (res != ERROR_SUCCESS) + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry key for the event log", (res)); + + auto_hkey_close hkey_guard(hkey); + + if (disposition != REG_OPENED_EXISTING_KEY || force) + { + // Fill registry values + if (!!params.event_message_file) + { + // Set the module file name that contains event resources + string_type const& module_name = params.event_message_file.get(); + res = registry::set_value( + hkey, + registry::get_event_message_file_param_name(), + 0, + REG_EXPAND_SZ, + reinterpret_cast< LPBYTE >(const_cast< CharT* >(module_name.c_str())), + static_cast< DWORD >((module_name.size() + 1) * sizeof(CharT))); + if (res != ERROR_SUCCESS) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value " + + log::aux::to_narrow(string_type(registry::get_event_message_file_param_name())), (res)); + } + } + + if (!!params.category_message_file) + { + // Set the module file name that contains event category resources + string_type const& module_name = params.category_message_file.get(); + res = registry::set_value( + hkey, + registry::get_category_message_file_param_name(), + 0, + REG_SZ, + reinterpret_cast< LPBYTE >(const_cast< CharT* >(module_name.c_str())), + static_cast< DWORD >((module_name.size() + 1) * sizeof(CharT))); + if (res != ERROR_SUCCESS) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value " + + log::aux::to_narrow(string_type(registry::get_category_message_file_param_name())), (res)); + } + } + + if (!!params.category_count) + { + // Set number of categories + DWORD category_count = params.category_count.get(); + res = registry::set_value( + hkey, + registry::get_category_count_param_name(), + 0, + REG_DWORD, + reinterpret_cast< LPBYTE >(&category_count), + static_cast< DWORD >(sizeof(category_count))); + if (res != ERROR_SUCCESS) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value " + + log::aux::to_narrow(string_type(registry::get_category_count_param_name())), (res)); + } + } + + if (!!params.types_supported) + { + // Set the supported event types + DWORD event_types = params.types_supported.get(); + res = registry::set_value( + hkey, + registry::get_types_supported_param_name(), + 0, + REG_DWORD, + reinterpret_cast< LPBYTE >(&event_types), + static_cast< DWORD >(sizeof(event_types))); + if (res != ERROR_SUCCESS) + { + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value " + + log::aux::to_narrow(string_type(registry::get_types_supported_param_name())), (res)); + } + } + } +} + +} // namespace aux + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WINDOWS_EVENT_LOG_REGISTRY_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/windows/ipc_reliable_message_queue.cpp b/src/boost/libs/log/src/windows/ipc_reliable_message_queue.cpp new file mode 100644 index 00000000..d99d091d --- /dev/null +++ b/src/boost/libs/log/src/windows/ipc_reliable_message_queue.cpp @@ -0,0 +1,818 @@ +/* + * Copyright Lingxi Li 2015. + * Copyright Andrey Semashev 2016. + * 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 ipc_reliable_message_queue_win.hpp + * \author Lingxi Li + * \author Andrey Semashev + * \date 28.10.2015 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * This file provides an interprocess message queue implementation on POSIX platforms. + */ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <cstring> +#include <new> +#include <limits> +#include <string> +#include <algorithm> +#include <stdexcept> +#include <boost/assert.hpp> +#include <boost/static_assert.hpp> +#include <boost/cstdint.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/atomic/capabilities.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/ipc/reliable_message_queue.hpp> +#include <boost/log/support/exception.hpp> +#include <boost/log/detail/pause.hpp> +#include <boost/exception/info.hpp> +#include <boost/exception/enable_error_info.hpp> +#include <boost/align/align_up.hpp> +#include <boost/winapi/thread.hpp> // SwitchToThread +#include "windows/ipc_sync_wrappers.hpp" +#include "windows/mapped_shared_memory.hpp" +#include "windows/utf_code_conversion.hpp" +#include "murmur3.hpp" +#include "bit_tools.hpp" +#include <windows.h> +#include <boost/log/detail/header.hpp> + +#if BOOST_ATOMIC_INT32_LOCK_FREE != 2 +// 32-bit atomic ops are required to be able to place atomic<uint32_t> in the process-shared memory +#error Boost.Log: Native 32-bit atomic operations are required but not supported by Boost.Atomic on the target platform +#endif + +//! A suffix used in names of interprocess objects created by the queue. +//! Used as a protection against clashing with user-supplied names of interprocess queues and also to resolve conflicts between queues of different types. +#define BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".3010b9950926463398eee00b35b44651" + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +//! Message queue implementation data +struct reliable_message_queue::implementation +{ +private: + //! Header of an allocation block within the message queue. Placed at the beginning of the block within the shared memory segment. + struct block_header + { + // Element data alignment, in bytes + enum { data_alignment = 32u }; + + //! Size of the element data, in bytes + size_type m_size; + + //! Returns the block header overhead, in bytes + static BOOST_CONSTEXPR size_type get_header_overhead() BOOST_NOEXCEPT + { + return static_cast< size_type >(boost::alignment::align_up(sizeof(block_header), data_alignment)); + } + + //! Returns a pointer to the element data + void* get_data() const BOOST_NOEXCEPT + { + return const_cast< unsigned char* >(reinterpret_cast< const unsigned char* >(this)) + get_header_overhead(); + } + }; + + //! Header of the message queue. Placed at the beginning of the shared memory segment. + struct header + { + // Increment this constant whenever you change the binary layout of the queue (apart from this header structure) + enum { abi_version = 0 }; + + // !!! Whenever you add/remove members in this structure, also modify get_abi_tag() function accordingly !!! + + //! A tag value to ensure the correct binary layout of the message queue data structures. Must be placed first and always have a fixed size and alignment. + uint32_t m_abi_tag; + //! Padding to protect against alignment changes in Boost.Atomic. Don't use BOOST_ALIGNMENT to ensure portability. + unsigned char m_padding[BOOST_LOG_CPU_CACHE_LINE_SIZE - sizeof(uint32_t)]; + //! A flag indicating that the queue is constructed (i.e. the queue is constructed when the value is not 0). + boost::atomic< uint32_t > m_initialized; + //! Number of allocation blocks in the queue. + const uint32_t m_capacity; + //! Size of an allocation block, in bytes. + const size_type m_block_size; + //! Shared state of the mutex for protecting queue data structures. + boost::log::ipc::aux::interprocess_mutex::shared_state m_mutex_state; + //! Shared state of the condition variable used to block writers when the queue is full. + boost::log::ipc::aux::interprocess_condition_variable::shared_state m_nonfull_queue_state; + //! The current number of allocated blocks in the queue. + uint32_t m_size; + //! The current writing position (allocation block index). + uint32_t m_put_pos; + //! The current reading position (allocation block index). + uint32_t m_get_pos; + + header(uint32_t capacity, size_type block_size) : + m_abi_tag(get_abi_tag()), + m_capacity(capacity), + m_block_size(block_size), + m_size(0u), + m_put_pos(0u), + m_get_pos(0u) + { + // Must be initialized last. m_initialized is zero-initialized initially. + m_initialized.fetch_add(1u, boost::memory_order_release); + } + + //! Returns the header structure ABI tag + static uint32_t get_abi_tag() BOOST_NOEXCEPT + { + // This FOURCC identifies the queue type + boost::log::aux::murmur3_32 hash(boost::log::aux::make_fourcc('r', 'e', 'l', 'q')); + + // This FOURCC identifies the queue implementation + hash.mix(boost::log::aux::make_fourcc('w', 'n', 't', '5')); + hash.mix(abi_version); + + // We will use these constants to align pointers + hash.mix(BOOST_LOG_CPU_CACHE_LINE_SIZE); + hash.mix(block_header::data_alignment); + + // The members in the sequence below must be enumerated in the same order as they are declared in the header structure. + // The ABI tag is supposed change whenever a member changes size or offset from the beginning of the header. + +#define BOOST_LOG_MIX_HEADER_MEMBER(name)\ + hash.mix(static_cast< uint32_t >(sizeof(((header*)NULL)->name)));\ + hash.mix(static_cast< uint32_t >(offsetof(header, name))) + + BOOST_LOG_MIX_HEADER_MEMBER(m_abi_tag); + BOOST_LOG_MIX_HEADER_MEMBER(m_padding); + BOOST_LOG_MIX_HEADER_MEMBER(m_initialized); + BOOST_LOG_MIX_HEADER_MEMBER(m_capacity); + BOOST_LOG_MIX_HEADER_MEMBER(m_block_size); + BOOST_LOG_MIX_HEADER_MEMBER(m_mutex_state); + BOOST_LOG_MIX_HEADER_MEMBER(m_nonfull_queue_state); + BOOST_LOG_MIX_HEADER_MEMBER(m_size); + BOOST_LOG_MIX_HEADER_MEMBER(m_put_pos); + BOOST_LOG_MIX_HEADER_MEMBER(m_get_pos); + +#undef BOOST_LOG_MIX_HEADER_MEMBER + + return hash.finalize(); + } + + //! Returns an element header at the specified index + block_header* get_block(uint32_t index) const BOOST_NOEXCEPT + { + BOOST_ASSERT(index < m_capacity); + unsigned char* p = const_cast< unsigned char* >(reinterpret_cast< const unsigned char* >(this)) + boost::alignment::align_up(sizeof(header), BOOST_LOG_CPU_CACHE_LINE_SIZE); + p += static_cast< std::size_t >(m_block_size) * static_cast< std::size_t >(index); + return reinterpret_cast< block_header* >(p); + } + + BOOST_DELETED_FUNCTION(header(header const&)) + BOOST_DELETED_FUNCTION(header& operator=(header const&)) + }; + +private: + //! Shared memory object and mapping + boost::log::ipc::aux::mapped_shared_memory m_shared_memory; + //! Queue overflow handling policy + const overflow_policy m_overflow_policy; + //! The mask for selecting bits that constitute size values from 0 to (block_size - 1) + size_type m_block_size_mask; + //! The number of the bit set in block_size (i.e. log base 2 of block_size) + uint32_t m_block_size_log2; + + //! Mutex for protecting queue data structures. + boost::log::ipc::aux::interprocess_mutex m_mutex; + //! Event used to block readers when the queue is empty. + boost::log::ipc::aux::interprocess_event m_nonempty_queue; + //! Condition variable used to block writers when the queue is full. + boost::log::ipc::aux::interprocess_condition_variable m_nonfull_queue; + //! The event indicates that stop has been requested + boost::log::ipc::aux::auto_handle m_stop; + + //! The queue name, as specified by the user + const object_name m_name; + +public: + //! The constructor creates a new shared memory segment + implementation + ( + open_mode::create_only_tag, + object_name const& name, + uint32_t capacity, + size_type block_size, + overflow_policy oflow_policy, + permissions const& perms + ) : + m_overflow_policy(oflow_policy), + m_block_size_mask(0u), + m_block_size_log2(0u), + m_name(name) + { + const std::wstring wname = boost::log::aux::utf8_to_utf16(name.c_str()); + const std::size_t shmem_size = estimate_region_size(capacity, block_size); + m_shared_memory.create(wname.c_str(), shmem_size, perms); + m_shared_memory.map(); + + create_queue(wname, capacity, block_size, perms); + } + + //! The constructor creates a new shared memory segment or opens the existing one + implementation + ( + open_mode::open_or_create_tag, + object_name const& name, + uint32_t capacity, + size_type block_size, + overflow_policy oflow_policy, + permissions const& perms + ) : + m_overflow_policy(oflow_policy), + m_block_size_mask(0u), + m_block_size_log2(0u), + m_name(name) + { + const std::wstring wname = boost::log::aux::utf8_to_utf16(name.c_str()); + const std::size_t shmem_size = estimate_region_size(capacity, block_size); + const bool created = m_shared_memory.create_or_open(wname.c_str(), shmem_size, perms); + m_shared_memory.map(); + + if (created) + create_queue(wname, capacity, block_size, perms); + else + adopt_queue(wname, m_shared_memory.size(), perms); + } + + //! The constructor opens the existing shared memory segment + implementation + ( + open_mode::open_only_tag, + object_name const& name, + overflow_policy oflow_policy, + permissions const& perms + ) : + m_overflow_policy(oflow_policy), + m_block_size_mask(0u), + m_block_size_log2(0u), + m_name(name) + { + const std::wstring wname = boost::log::aux::utf8_to_utf16(name.c_str()); + m_shared_memory.open(wname.c_str()); + m_shared_memory.map(); + + adopt_queue(wname, m_shared_memory.size(), perms); + } + + object_name const& name() const BOOST_NOEXCEPT + { + return m_name; + } + + uint32_t capacity() const BOOST_NOEXCEPT + { + return get_header()->m_capacity; + } + + size_type block_size() const BOOST_NOEXCEPT + { + return get_header()->m_block_size; + } + + operation_result send(void const* message_data, size_type message_size) + { + const uint32_t block_count = estimate_block_count(message_size); + + header* const hdr = get_header(); + + if (BOOST_UNLIKELY(block_count > hdr->m_capacity)) + BOOST_LOG_THROW_DESCR(logic_error, "Message size exceeds the interprocess queue capacity"); + + if (!lock_queue()) + return aborted; + + boost::log::ipc::aux::interprocess_mutex::optional_unlock unlock(m_mutex); + + while (true) + { + if ((hdr->m_capacity - hdr->m_size) >= block_count) + break; + + const overflow_policy oflow_policy = m_overflow_policy; + if (oflow_policy == fail_on_overflow) + return no_space; + else if (BOOST_UNLIKELY(oflow_policy == throw_on_overflow)) + BOOST_LOG_THROW_DESCR(capacity_limit_reached, "Interprocess queue is full"); + + if (!m_nonfull_queue.wait(unlock, m_stop.get())) + return aborted; + } + + enqueue_message(message_data, message_size, block_count); + + return succeeded; + } + + bool try_send(void const* message_data, size_type message_size) + { + const uint32_t block_count = estimate_block_count(message_size); + + header* const hdr = get_header(); + + if (BOOST_UNLIKELY(block_count > hdr->m_capacity)) + BOOST_LOG_THROW_DESCR(logic_error, "Message size exceeds the interprocess queue capacity"); + + if (!lock_queue()) + return false; + + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(m_mutex); + + if ((hdr->m_capacity - hdr->m_size) < block_count) + return false; + + enqueue_message(message_data, message_size, block_count); + + return true; + } + + operation_result receive(receive_handler handler, void* state) + { + if (!lock_queue()) + return aborted; + + boost::log::ipc::aux::interprocess_mutex::optional_unlock unlock(m_mutex); + + header* const hdr = get_header(); + + while (true) + { + if (hdr->m_size > 0u) + break; + + m_mutex.unlock(); + unlock.disengage(); + + if (!m_nonempty_queue.wait(m_stop.get()) || !lock_queue()) + return aborted; + + unlock.engage(m_mutex); + } + + dequeue_message(handler, state); + + return succeeded; + } + + bool try_receive(receive_handler handler, void* state) + { + if (!lock_queue()) + return false; + + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(m_mutex); + + header* const hdr = get_header(); + if (hdr->m_size == 0u) + return false; + + dequeue_message(handler, state); + + return true; + } + + void stop_local() + { + BOOST_VERIFY(boost::winapi::SetEvent(m_stop.get()) != 0); + } + + void reset_local() + { + BOOST_VERIFY(boost::winapi::ResetEvent(m_stop.get()) != 0); + } + + void clear() + { + m_mutex.lock(); + boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(m_mutex); + clear_queue(); + } + +private: + header* get_header() const BOOST_NOEXCEPT + { + return static_cast< header* >(m_shared_memory.address()); + } + + static std::size_t estimate_region_size(uint32_t capacity, size_type block_size) BOOST_NOEXCEPT + { + return boost::alignment::align_up(sizeof(header), BOOST_LOG_CPU_CACHE_LINE_SIZE) + static_cast< std::size_t >(capacity) * static_cast< std::size_t >(block_size); + } + + void create_stop_event() + { +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + boost::winapi::HANDLE_ h = boost::winapi::CreateEventExW + ( + NULL, // permissions + NULL, // name + boost::winapi::CREATE_EVENT_MANUAL_RESET_, + boost::winapi::SYNCHRONIZE_ | boost::winapi::EVENT_MODIFY_STATE_ + ); +#else + boost::winapi::HANDLE_ h = boost::winapi::CreateEventW + ( + NULL, // permissions + true, // manual reset + false, // initial state + NULL // name + ); +#endif + if (BOOST_UNLIKELY(h == NULL)) + { + boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to create an stop event object", (err)); + } + + m_stop.init(h); + } + + void create_queue(std::wstring const& name, uint32_t capacity, size_type block_size, permissions const& perms) + { + // Initialize synchronization primitives before initializing the header as the openers will wait for it to be initialized + header* const hdr = get_header(); + m_mutex.create((name + BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".mutex").c_str(), &hdr->m_mutex_state, perms); + m_nonempty_queue.create((name + BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".nonempty_queue_event").c_str(), false, perms); + m_nonfull_queue.init((name + BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".nonfull_queue_cond_var").c_str(), &hdr->m_nonfull_queue_state, perms); + create_stop_event(); + + new (hdr) header(capacity, block_size); + + init_block_size(block_size); + } + + void adopt_queue(std::wstring const& name, std::size_t shmem_size, permissions const& perms) + { + if (shmem_size < sizeof(header)) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment size too small"); + + // Wait until the mapped region becomes initialized + header* const hdr = get_header(); + BOOST_CONSTEXPR_OR_CONST unsigned int wait_loops = 1000u, spin_loops = 16u, spins = 16u; + for (unsigned int i = 0; i < wait_loops; ++i) + { + uint32_t initialized = hdr->m_initialized.load(boost::memory_order_acquire); + if (initialized) + { + goto done; + } + + if (i < spin_loops) + { + for (unsigned int j = 0; j < spins; ++j) + { + boost::log::aux::pause(); + } + } + else + { + boost::winapi::SwitchToThread(); + } + } + + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment is not initialized by creator for too long"); + + done: + // Check that the queue layout matches the current process ABI + if (hdr->m_abi_tag != header::get_abi_tag()) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: the queue ABI is incompatible"); + + if (!boost::log::aux::is_power_of_2(hdr->m_block_size)) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: the queue block size is not a power of 2"); + + m_mutex.open((name + BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".mutex").c_str(), &hdr->m_mutex_state); + m_nonempty_queue.open((name + BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".nonempty_queue_event").c_str()); + m_nonfull_queue.init((name + BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".nonfull_queue_cond_var").c_str(), &hdr->m_nonfull_queue_state, perms); + create_stop_event(); + + init_block_size(hdr->m_block_size); + } + + void init_block_size(size_type block_size) + { + m_block_size_mask = block_size - 1u; + + uint32_t block_size_log2 = 0u; + if ((block_size & 0x0000ffff) == 0u) + { + block_size >>= 16u; + block_size_log2 += 16u; + } + if ((block_size & 0x000000ff) == 0u) + { + block_size >>= 8u; + block_size_log2 += 8u; + } + if ((block_size & 0x0000000f) == 0u) + { + block_size >>= 4u; + block_size_log2 += 4u; + } + if ((block_size & 0x00000003) == 0u) + { + block_size >>= 2u; + block_size_log2 += 2u; + } + if ((block_size & 0x00000001) == 0u) + { + ++block_size_log2; + } + m_block_size_log2 = block_size_log2; + } + + bool lock_queue() + { + return m_mutex.lock(m_stop.get()); + } + + void clear_queue() + { + header* const hdr = get_header(); + hdr->m_size = 0u; + hdr->m_put_pos = 0u; + hdr->m_get_pos = 0u; + m_nonfull_queue.notify_all(); + } + + //! Returns the number of allocation blocks that are required to store user's payload of the specified size + uint32_t estimate_block_count(size_type size) const BOOST_NOEXCEPT + { + // ceil((size + get_header_overhead()) / block_size) + return static_cast< uint32_t >((size + block_header::get_header_overhead() + m_block_size_mask) >> m_block_size_log2); + } + + //! Puts the message to the back of the queue + void enqueue_message(void const* message_data, size_type message_size, uint32_t block_count) + { + header* const hdr = get_header(); + + const uint32_t capacity = hdr->m_capacity; + const size_type block_size = hdr->m_block_size; + uint32_t pos = hdr->m_put_pos; + + block_header* block = hdr->get_block(pos); + block->m_size = message_size; + + size_type write_size = (std::min)(static_cast< size_type >((capacity - pos) * block_size - block_header::get_header_overhead()), message_size); + std::memcpy(block->get_data(), message_data, write_size); + + pos += block_count; + if (BOOST_UNLIKELY(pos >= capacity)) + { + // Write the rest of the message at the beginning of the queue + pos -= capacity; + message_data = static_cast< const unsigned char* >(message_data) + write_size; + write_size = message_size - write_size; + if (write_size > 0u) + std::memcpy(hdr->get_block(0u), message_data, write_size); + } + + hdr->m_put_pos = pos; + + const uint32_t old_queue_size = hdr->m_size; + hdr->m_size = old_queue_size + block_count; + if (old_queue_size == 0u) + m_nonempty_queue.set(); + } + + //! Retrieves the next message and invokes the handler to store the message contents + void dequeue_message(receive_handler handler, void* state) + { + header* const hdr = get_header(); + + const uint32_t capacity = hdr->m_capacity; + const size_type block_size = hdr->m_block_size; + uint32_t pos = hdr->m_get_pos; + + block_header* block = hdr->get_block(pos); + size_type message_size = block->m_size; + uint32_t block_count = estimate_block_count(message_size); + + BOOST_ASSERT(block_count <= hdr->m_size); + + size_type read_size = (std::min)(static_cast< size_type >((capacity - pos) * block_size - block_header::get_header_overhead()), message_size); + handler(state, block->get_data(), read_size); + + pos += block_count; + if (BOOST_UNLIKELY(pos >= capacity)) + { + // Read the tail of the message + pos -= capacity; + read_size = message_size - read_size; + if (read_size > 0u) + handler(state, hdr->get_block(0u), read_size); + } + + hdr->m_get_pos = pos; + hdr->m_size -= block_count; + + m_nonfull_queue.notify_all(); + } +}; + +BOOST_LOG_API void reliable_message_queue::create(object_name const& name, uint32_t capacity, size_type block_size, overflow_policy oflow_policy, permissions const& perms) +{ + BOOST_ASSERT(m_impl == NULL); + if (!boost::log::aux::is_power_of_2(block_size)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Interprocess message queue block size is not a power of 2")); + try + { + m_impl = new implementation(open_mode::create_only, name, capacity, static_cast< size_type >(boost::alignment::align_up(block_size, BOOST_LOG_CPU_CACHE_LINE_SIZE)), oflow_policy, perms); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(name); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::open_or_create(object_name const& name, uint32_t capacity, size_type block_size, overflow_policy oflow_policy, permissions const& perms) +{ + BOOST_ASSERT(m_impl == NULL); + if (!boost::log::aux::is_power_of_2(block_size)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Interprocess message queue block size is not a power of 2")); + try + { + m_impl = new implementation(open_mode::open_or_create, name, capacity, static_cast< size_type >(boost::alignment::align_up(block_size, BOOST_LOG_CPU_CACHE_LINE_SIZE)), oflow_policy, perms); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(name); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::open(object_name const& name, overflow_policy oflow_policy, permissions const& perms) +{ + BOOST_ASSERT(m_impl == NULL); + try + { + m_impl = new implementation(open_mode::open_only, name, oflow_policy, perms); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(name); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::clear() +{ + BOOST_ASSERT(m_impl != NULL); + try + { + m_impl->clear(); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API object_name const& reliable_message_queue::name() const +{ + BOOST_ASSERT(m_impl != NULL); + return m_impl->name(); +} + +BOOST_LOG_API uint32_t reliable_message_queue::capacity() const +{ + BOOST_ASSERT(m_impl != NULL); + return m_impl->capacity(); +} + +BOOST_LOG_API reliable_message_queue::size_type reliable_message_queue::block_size() const +{ + BOOST_ASSERT(m_impl != NULL); + return m_impl->block_size(); +} + +BOOST_LOG_API void reliable_message_queue::stop_local() +{ + BOOST_ASSERT(m_impl != NULL); + try + { + m_impl->stop_local(); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::reset_local() +{ + BOOST_ASSERT(m_impl != NULL); + try + { + m_impl->reset_local(); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API void reliable_message_queue::do_close() BOOST_NOEXCEPT +{ + delete m_impl; + m_impl = NULL; +} + +BOOST_LOG_API reliable_message_queue::operation_result reliable_message_queue::send(void const* message_data, size_type message_size) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->send(message_data, message_size); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API bool reliable_message_queue::try_send(void const* message_data, size_type message_size) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->try_send(message_data, message_size); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API reliable_message_queue::operation_result reliable_message_queue::do_receive(receive_handler handler, void* state) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->receive(handler, state); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +BOOST_LOG_API bool reliable_message_queue::do_try_receive(receive_handler handler, void* state) +{ + BOOST_ASSERT(m_impl != NULL); + try + { + return m_impl->try_receive(handler, state); + } + catch (boost::exception& e) + { + e << boost::log::ipc::object_name_info(m_impl->name()); + throw; + } +} + +//! Fixed buffer receive handler +BOOST_LOG_API void reliable_message_queue::fixed_buffer_receive_handler(void* state, const void* data, size_type size) +{ + fixed_buffer_state* p = static_cast< fixed_buffer_state* >(state); + if (BOOST_UNLIKELY(size > p->size)) + BOOST_THROW_EXCEPTION(bad_alloc("Buffer too small to receive the message")); + + std::memcpy(p->data, data, size); + p->data += size; + p->size -= size; +} + +BOOST_LOG_API void reliable_message_queue::remove(object_name const&) +{ + // System objects are reference counted on Windows, nothing to do here +} + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/windows/ipc_sync_wrappers.cpp b/src/boost/libs/log/src/windows/ipc_sync_wrappers.cpp new file mode 100644 index 00000000..bd8bfb21 --- /dev/null +++ b/src/boost/libs/log/src/windows/ipc_sync_wrappers.cpp @@ -0,0 +1,544 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/ipc_sync_wrappers.cpp + * \author Andrey Semashev + * \date 23.01.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/winapi/access_rights.hpp> +#include <boost/winapi/handles.hpp> +#include <boost/winapi/event.hpp> +#include <boost/winapi/semaphore.hpp> +#include <boost/winapi/wait.hpp> +#include <boost/winapi/dll.hpp> +#include <boost/winapi/time.hpp> +#include <boost/winapi/get_last_error.hpp> +#include <boost/winapi/character_code_conversion.hpp> +#include <windows.h> // for error codes +#include <cstddef> +#include <limits> +#include <string> +#include <utility> +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +#include <boost/checked_delete.hpp> +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/log/detail/snprintf.hpp> +#include "unique_ptr.hpp" +#include "windows/ipc_sync_wrappers.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Hex character table, defined in dump.cpp +extern const char g_hex_char_table[2][16]; + +} // namespace aux + +namespace ipc { + +namespace aux { + +void interprocess_event::create(const wchar_t* name, bool manual_reset, permissions const& perms) +{ +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + boost::winapi::HANDLE_ h = boost::winapi::CreateEventExW + ( + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + name, + boost::winapi::CREATE_EVENT_MANUAL_RESET_ * manual_reset, + boost::winapi::SYNCHRONIZE_ | boost::winapi::EVENT_MODIFY_STATE_ + ); +#else + boost::winapi::HANDLE_ h = boost::winapi::CreateEventW + ( + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + manual_reset, + false, + name + ); +#endif + if (BOOST_UNLIKELY(h == NULL)) + { + boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to create an interprocess event object", (err)); + } + + m_event.init(h); +} + +void interprocess_event::create_or_open(const wchar_t* name, bool manual_reset, permissions const& perms) +{ +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + boost::winapi::HANDLE_ h = boost::winapi::CreateEventExW + ( + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + name, + boost::winapi::CREATE_EVENT_MANUAL_RESET_ * manual_reset, + boost::winapi::SYNCHRONIZE_ | boost::winapi::EVENT_MODIFY_STATE_ + ); +#else + boost::winapi::HANDLE_ h = boost::winapi::CreateEventW + ( + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + manual_reset, + false, + name + ); +#endif + if (h == NULL) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + if (BOOST_LIKELY(err == ERROR_ALREADY_EXISTS)) + { + open(name); + return; + } + else + { + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to create an interprocess event object", (err)); + } + } + + m_event.init(h); +} + +void interprocess_event::open(const wchar_t* name) +{ + boost::winapi::HANDLE_ h = boost::winapi::OpenEventW(boost::winapi::SYNCHRONIZE_ | boost::winapi::EVENT_MODIFY_STATE_, false, name); + if (BOOST_UNLIKELY(h == NULL)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to open an interprocess event object", (err)); + } + + m_event.init(h); +} + +boost::atomic< interprocess_semaphore::is_semaphore_zero_count_t > interprocess_semaphore::is_semaphore_zero_count(&interprocess_semaphore::is_semaphore_zero_count_init); +interprocess_semaphore::nt_query_semaphore_t interprocess_semaphore::nt_query_semaphore = NULL; + +void interprocess_semaphore::create_or_open(const wchar_t* name, permissions const& perms) +{ +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + boost::winapi::HANDLE_ h = boost::winapi::CreateSemaphoreExW + ( + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + 0, // initial count + (std::numeric_limits< boost::winapi::LONG_ >::max)(), // max count + name, + 0u, // flags + boost::winapi::SYNCHRONIZE_ | boost::winapi::SEMAPHORE_MODIFY_STATE_ | boost::winapi::SEMAPHORE_QUERY_STATE_ + ); +#else + boost::winapi::HANDLE_ h = boost::winapi::CreateSemaphoreW + ( + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + 0, // initial count + (std::numeric_limits< boost::winapi::LONG_ >::max)(), // max count + name + ); +#endif + if (h == NULL) + { + boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + if (BOOST_LIKELY(err == ERROR_ALREADY_EXISTS)) + { + open(name); + return; + } + else + { + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to create an interprocess semaphore object", (err)); + } + } + + m_sem.init(h); +} + +void interprocess_semaphore::open(const wchar_t* name) +{ + boost::winapi::HANDLE_ h = boost::winapi::OpenSemaphoreW(boost::winapi::SYNCHRONIZE_ | boost::winapi::SEMAPHORE_MODIFY_STATE_ | boost::winapi::SEMAPHORE_QUERY_STATE_, false, name); + if (BOOST_UNLIKELY(h == NULL)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to open an interprocess semaphore object", (err)); + } + + m_sem.init(h); +} + +bool interprocess_semaphore::is_semaphore_zero_count_init(boost::winapi::HANDLE_ h) +{ + is_semaphore_zero_count_t impl = &interprocess_semaphore::is_semaphore_zero_count_emulated; + + // Check if ntdll.dll provides NtQuerySemaphore, see: http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FSemaphore%2FNtQuerySemaphore.html + boost::winapi::HMODULE_ ntdll = boost::winapi::GetModuleHandleW(L"ntdll.dll"); + if (ntdll) + { + nt_query_semaphore_t ntqs = (nt_query_semaphore_t)boost::winapi::get_proc_address(ntdll, "NtQuerySemaphore"); + if (ntqs) + { + nt_query_semaphore = ntqs; + impl = &interprocess_semaphore::is_semaphore_zero_count_nt_query_semaphore; + } + } + + is_semaphore_zero_count.store(impl, boost::memory_order_release); + + return impl(h); +} + +bool interprocess_semaphore::is_semaphore_zero_count_nt_query_semaphore(boost::winapi::HANDLE_ h) +{ + semaphore_basic_information info = {}; + NTSTATUS_ err = nt_query_semaphore + ( + h, + 0u, // SemaphoreBasicInformation + &info, + sizeof(info), + NULL + ); + if (BOOST_UNLIKELY(err != 0u)) + { + char buf[sizeof(unsigned int) * 2u + 4u]; + boost::log::aux::snprintf(buf, sizeof(buf), "0x%08x", static_cast< unsigned int >(err)); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, std::string("Failed to test an interprocess semaphore object for zero count, NT status: ") + buf, (ERROR_INVALID_HANDLE)); + } + + return info.current_count == 0u; +} + +bool interprocess_semaphore::is_semaphore_zero_count_emulated(boost::winapi::HANDLE_ h) +{ + const boost::winapi::DWORD_ retval = boost::winapi::WaitForSingleObject(h, 0u); + if (retval == boost::winapi::wait_timeout) + { + return true; + } + else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to test an interprocess semaphore object for zero count", (err)); + } + + // Restore the decremented counter + BOOST_VERIFY(!!boost::winapi::ReleaseSemaphore(h, 1, NULL)); + + return false; +} + +#if !defined(BOOST_MSVC) || _MSC_VER >= 1800 +BOOST_CONSTEXPR_OR_CONST uint32_t interprocess_mutex::lock_flag_bit; +BOOST_CONSTEXPR_OR_CONST uint32_t interprocess_mutex::event_set_flag_bit; +BOOST_CONSTEXPR_OR_CONST uint32_t interprocess_mutex::lock_flag_value; +BOOST_CONSTEXPR_OR_CONST uint32_t interprocess_mutex::event_set_flag_value; +BOOST_CONSTEXPR_OR_CONST uint32_t interprocess_mutex::waiter_count_mask; +#endif + +void interprocess_mutex::lock_slow() +{ + uint32_t old_state = m_shared_state->m_lock_state.load(boost::memory_order_relaxed); + mark_waiting_and_try_lock(old_state); + + if ((old_state & lock_flag_value) != 0u) try + { + do + { + m_event.wait(); + clear_waiting_and_try_lock(old_state); + } + while ((old_state & lock_flag_value) != 0u); + } + catch (...) + { + m_shared_state->m_lock_state.fetch_sub(1u, boost::memory_order_acq_rel); + throw; + } +} + +bool interprocess_mutex::lock_slow(boost::winapi::HANDLE_ abort_handle) +{ + uint32_t old_state = m_shared_state->m_lock_state.load(boost::memory_order_relaxed); + mark_waiting_and_try_lock(old_state); + + if ((old_state & lock_flag_value) != 0u) try + { + do + { + if (!m_event.wait(abort_handle)) + { + // Wait was interrupted + m_shared_state->m_lock_state.fetch_sub(1u, boost::memory_order_acq_rel); + return false; + } + + clear_waiting_and_try_lock(old_state); + } + while ((old_state & lock_flag_value) != 0u); + } + catch (...) + { + m_shared_state->m_lock_state.fetch_sub(1u, boost::memory_order_acq_rel); + throw; + } + + return true; +} + +inline void interprocess_mutex::mark_waiting_and_try_lock(uint32_t& old_state) +{ + uint32_t new_state; + do + { + uint32_t was_locked = (old_state & lock_flag_value); + if (was_locked) + { + // Avoid integer overflows + if (BOOST_UNLIKELY((old_state & waiter_count_mask) == waiter_count_mask)) + BOOST_LOG_THROW_DESCR(limitation_error, "Too many waiters on an interprocess mutex"); + + new_state = old_state + 1u; + } + else + { + new_state = old_state | lock_flag_value; + } + } + while (!m_shared_state->m_lock_state.compare_exchange_weak(old_state, new_state, boost::memory_order_acq_rel, boost::memory_order_relaxed)); +} + +inline void interprocess_mutex::clear_waiting_and_try_lock(uint32_t& old_state) +{ + old_state &= ~lock_flag_value; + old_state |= event_set_flag_value; + uint32_t new_state; + do + { + new_state = ((old_state & lock_flag_value) ? old_state : ((old_state - 1u) | lock_flag_value)) & ~event_set_flag_value; + } + while (!m_shared_state->m_lock_state.compare_exchange_strong(old_state, new_state, boost::memory_order_acq_rel, boost::memory_order_relaxed)); +} + + +bool interprocess_condition_variable::wait(interprocess_mutex::optional_unlock& lock, boost::winapi::HANDLE_ abort_handle) +{ + int32_t waiters = m_shared_state->m_waiters; + if (waiters < 0) + { + // We need to select a new semaphore to block on + m_current_semaphore = get_unused_semaphore(); + ++m_shared_state->m_generation; + m_shared_state->m_semaphore_id = m_current_semaphore->m_id; + waiters = 0; + } + else + { + // Avoid integer overflow + if (BOOST_UNLIKELY(waiters >= ((std::numeric_limits< int32_t >::max)() - 1))) + BOOST_LOG_THROW_DESCR(limitation_error, "Too many waiters on an interprocess condition variable"); + + // Make sure we use the right semaphore to block on + const uint32_t id = m_shared_state->m_semaphore_id; + if (m_current_semaphore->m_id != id) + m_current_semaphore = get_semaphore(id); + } + + m_shared_state->m_waiters = waiters + 1; + const uint32_t generation = m_shared_state->m_generation; + + boost::winapi::HANDLE_ handles[2u] = { m_current_semaphore->m_semaphore.get_handle(), abort_handle }; + + interprocess_mutex* const mutex = lock.disengage(); + mutex->unlock(); + + boost::winapi::DWORD_ retval = boost::winapi::WaitForMultipleObjects(2u, handles, false, boost::winapi::INFINITE_); + + if (BOOST_UNLIKELY(retval == boost::winapi::WAIT_FAILED_)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + + // Although highly unrealistic, it is possible that it took so long for the current thread to enter WaitForMultipleObjects that + // another thread has managed to destroy the semaphore. This can happen if the semaphore remains in a non-zero state + // for too long, which means that another process died while being blocked on the semaphore, and the semaphore was signalled, + // and the non-zero state timeout has passed. In this case the most logical behavior for the wait function is to return as + // if because of a wakeup. + if (err == ERROR_INVALID_HANDLE) + retval = boost::winapi::WAIT_OBJECT_0_; + else + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess semaphore object", (err)); + } + + // Have to unconditionally lock the mutex here + mutex->lock(); + lock.engage(*mutex); + + if (generation == m_shared_state->m_generation && m_shared_state->m_waiters > 0) + --m_shared_state->m_waiters; + + return retval == boost::winapi::WAIT_OBJECT_0_; +} + +//! Finds or opens a semaphore with the specified id +interprocess_condition_variable::semaphore_info* interprocess_condition_variable::get_semaphore(uint32_t id) +{ + semaphore_info_set::insert_commit_data insert_state; + std::pair< semaphore_info_set::iterator, bool > res = m_semaphore_info_set.insert_check(id, semaphore_info::order_by_id(), insert_state); + if (res.second) + { + // We need to open the semaphore. It is possible that the semaphore does not exist because all processes that had it opened terminated. + // Because of this we also attempt to create it. + boost::log::aux::unique_ptr< semaphore_info > p(new semaphore_info(id)); + generate_semaphore_name(id); + p->m_semaphore.create_or_open(m_semaphore_name.c_str(), m_perms); + + res.first = m_semaphore_info_set.insert_commit(*p, insert_state); + m_semaphore_info_list.push_back(*p); + + return p.release(); + } + else + { + // Move the semaphore to the end of the list so that the next time we are less likely to use it + semaphore_info& info = *res.first; + m_semaphore_info_list.erase(m_semaphore_info_list.iterator_to(info)); + m_semaphore_info_list.push_back(info); + + return &info; + } +} + +//! Finds or creates a semaphore with zero counter +interprocess_condition_variable::semaphore_info* interprocess_condition_variable::get_unused_semaphore() +{ + // Be optimistic, check the current semaphore first + if (m_current_semaphore && m_current_semaphore->m_semaphore.is_zero_count()) + { + mark_unused(*m_current_semaphore); + return m_current_semaphore; + } + + const tick_count_clock::time_point now = tick_count_clock::now(); + + semaphore_info_list::iterator it = m_semaphore_info_list.begin(), end = m_semaphore_info_list.end(); + while (it != end) + { + if (is_overflow_less(m_next_semaphore_id, it->m_id) || m_next_semaphore_id == it->m_id) + m_next_semaphore_id = it->m_id + 1u; + + if (it->m_semaphore.is_zero_count()) + { + semaphore_info& info = *it; + mark_unused(info); + return &info; + } + else if (it->check_non_zero_timeout(now)) + { + // The semaphore is non-zero for too long. A blocked process must have crashed. Close it. + m_semaphore_info_set.erase(m_semaphore_info_set.iterator_to(*it)); + m_semaphore_info_list.erase_and_dispose(it++, boost::checked_deleter< semaphore_info >()); + } + else + { + ++it; + } + } + + // No semaphore found, create a new one + for (uint32_t semaphore_id = m_next_semaphore_id, semaphore_id_end = semaphore_id - 1u; semaphore_id != semaphore_id_end; ++semaphore_id) + { + interprocess_semaphore sem; + try + { + generate_semaphore_name(semaphore_id); + sem.create_or_open(m_semaphore_name.c_str(), m_perms); + if (!sem.is_zero_count()) + continue; + } + catch (...) + { + // Ignore errors, try the next one + continue; + } + + semaphore_info* p = NULL; + semaphore_info_set::insert_commit_data insert_state; + std::pair< semaphore_info_set::iterator, bool > res = m_semaphore_info_set.insert_check(semaphore_id, semaphore_info::order_by_id(), insert_state); + if (res.second) + { + p = new semaphore_info(semaphore_id); + p->m_semaphore.swap(sem); + + res.first = m_semaphore_info_set.insert_commit(*p, insert_state); + m_semaphore_info_list.push_back(*p); + } + else + { + // Some of our currently open semaphores must have been released by another thread + p = &*res.first; + mark_unused(*p); + } + + m_next_semaphore_id = semaphore_id + 1u; + + return p; + } + + BOOST_LOG_THROW_DESCR(limitation_error, "Too many semaphores are actively used for an interprocess condition variable"); + BOOST_LOG_UNREACHABLE_RETURN(NULL); +} + +//! Marks the semaphore info as unused and moves to the end of list +inline void interprocess_condition_variable::mark_unused(semaphore_info& info) BOOST_NOEXCEPT +{ + // Restart the timeout for non-zero state next time we search for an unused semaphore + info.m_checked_for_zero = false; + // Move to the end of the list so that we consider this semaphore last + m_semaphore_info_list.erase(m_semaphore_info_list.iterator_to(info)); + m_semaphore_info_list.push_back(info); +} + +//! Generates semaphore name according to id +inline void interprocess_condition_variable::generate_semaphore_name(uint32_t id) BOOST_NOEXCEPT +{ + // Note: avoid anything that involves locale to make semaphore names as stable as possible + BOOST_ASSERT(m_semaphore_name.size() >= 8u); + + wchar_t* p = &m_semaphore_name[m_semaphore_name.size() - 8u]; + *p++ = boost::log::aux::g_hex_char_table[0][id >> 28]; + *p++ = boost::log::aux::g_hex_char_table[0][(id >> 24) & 0x0000000Fu]; + + *p++ = boost::log::aux::g_hex_char_table[0][(id >> 20) & 0x0000000Fu]; + *p++ = boost::log::aux::g_hex_char_table[0][(id >> 16) & 0x0000000Fu]; + + *p++ = boost::log::aux::g_hex_char_table[0][(id >> 12) & 0x0000000Fu]; + *p++ = boost::log::aux::g_hex_char_table[0][(id >> 8) & 0x0000000Fu]; + + *p++ = boost::log::aux::g_hex_char_table[0][(id >> 4) & 0x0000000Fu]; + *p = boost::log::aux::g_hex_char_table[0][id & 0x0000000Fu]; +} + +} // namespace aux + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/windows/ipc_sync_wrappers.hpp b/src/boost/libs/log/src/windows/ipc_sync_wrappers.hpp new file mode 100644 index 00000000..7d1272aa --- /dev/null +++ b/src/boost/libs/log/src/windows/ipc_sync_wrappers.hpp @@ -0,0 +1,652 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/ipc_sync_wrappers.hpp + * \author Andrey Semashev + * \date 23.01.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ +#define BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/winapi/access_rights.hpp> +#include <boost/winapi/handles.hpp> +#include <boost/winapi/event.hpp> +#include <boost/winapi/semaphore.hpp> +#include <boost/winapi/wait.hpp> +#include <boost/winapi/dll.hpp> +#include <boost/winapi/time.hpp> +#include <boost/winapi/get_last_error.hpp> +#include <cstddef> +#include <limits> +#include <string> +#include <utility> +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> +#include <boost/checked_delete.hpp> +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/intrusive/set.hpp> +#include <boost/intrusive/set_hook.hpp> +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/list_hook.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/permissions.hpp> +#include "windows/auto_handle.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +namespace aux { + +// TODO: Port to Boost.Atomic when it supports extended atomic ops +#if defined(BOOST_MSVC) && (_MSC_VER >= 1400) && !defined(UNDER_CE) + +#if _MSC_VER == 1400 +extern "C" unsigned char _interlockedbittestandset(long *a, long b); +extern "C" unsigned char _interlockedbittestandreset(long *a, long b); +#else +extern "C" unsigned char _interlockedbittestandset(volatile long *a, long b); +extern "C" unsigned char _interlockedbittestandreset(volatile long *a, long b); +#endif + +#pragma intrinsic(_interlockedbittestandset) +#pragma intrinsic(_interlockedbittestandreset) + +BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + return _interlockedbittestandset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0; +} + +BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + return _interlockedbittestandreset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0; +} + +#elif (defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN)) && defined(_M_IX86) + +BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + boost::atomic< uint32_t >::storage_type* p = &x.storage(); + bool ret; + __asm + { + mov eax, bit + mov edx, p + lock bts [edx], eax + setc ret + }; + return ret; +} + +BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + boost::atomic< uint32_t >::storage_type* p = &x.storage(); + bool ret; + __asm + { + mov eax, bit + mov edx, p + lock btr [edx], eax + setc ret + }; + return ret; +} + +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#if !defined(__CUDACC__) +#define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "cc", +#else +#define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA +#endif + +BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + bool res; + __asm__ __volatile__ + ( + "lock; bts %[bit_number], %[storage]\n\t" + "setc %[result]\n\t" + : [storage] "+m" (x.storage()), [result] "=q" (res) + : [bit_number] "Kq" (bit) + : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory" + ); + return res; +} + +BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + bool res; + __asm__ __volatile__ + ( + "lock; btr %[bit_number], %[storage]\n\t" + "setc %[result]\n\t" + : [storage] "+m" (x.storage()), [result] "=q" (res) + : [bit_number] "Kq" (bit) + : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory" + ); + return res; +} + +#else + +BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + const uint32_t mask = uint32_t(1u) << bit; + uint32_t old_val = x.fetch_or(mask, boost::memory_order_acq_rel); + return (old_val & mask) != 0u; +} + +BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT +{ + const uint32_t mask = uint32_t(1u) << bit; + uint32_t old_val = x.fetch_and(~mask, boost::memory_order_acq_rel); + return (old_val & mask) != 0u; +} + +#endif + +//! Interprocess event object +class interprocess_event +{ +private: + auto_handle m_event; + +public: + void create(const wchar_t* name, bool manual_reset, permissions const& perms = permissions()); + void create_or_open(const wchar_t* name, bool manual_reset, permissions const& perms = permissions()); + void open(const wchar_t* name); + + boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_event.get(); } + + void set() + { + if (BOOST_UNLIKELY(!boost::winapi::SetEvent(m_event.get()))) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to set an interprocess event object", (err)); + } + } + + void set_noexcept() BOOST_NOEXCEPT + { + BOOST_VERIFY(!!boost::winapi::SetEvent(m_event.get())); + } + + void reset() + { + if (BOOST_UNLIKELY(!boost::winapi::ResetEvent(m_event.get()))) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to reset an interprocess event object", (err)); + } + } + + void wait() + { + const boost::winapi::DWORD_ retval = boost::winapi::WaitForSingleObject(m_event.get(), boost::winapi::infinite); + if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess event object", (err)); + } + } + + bool wait(boost::winapi::HANDLE_ abort_handle) + { + boost::winapi::HANDLE_ handles[2u] = { m_event.get(), abort_handle }; + const boost::winapi::DWORD_ retval = boost::winapi::WaitForMultipleObjects(2u, handles, false, boost::winapi::infinite); + if (retval == (boost::winapi::wait_object_0 + 1u)) + { + // Wait was interrupted + return false; + } + else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess event object", (err)); + } + + return true; + } + + void swap(interprocess_event& that) BOOST_NOEXCEPT + { + m_event.swap(that.m_event); + } +}; + +//! Interprocess semaphore object +class interprocess_semaphore +{ +private: + typedef boost::winapi::DWORD_ NTSTATUS_; + struct semaphore_basic_information + { + boost::winapi::ULONG_ current_count; // current semaphore count + boost::winapi::ULONG_ maximum_count; // max semaphore count + }; + typedef NTSTATUS_ (__stdcall *nt_query_semaphore_t)(boost::winapi::HANDLE_ h, unsigned int info_class, semaphore_basic_information* pinfo, boost::winapi::ULONG_ info_size, boost::winapi::ULONG_* ret_len); + typedef bool (*is_semaphore_zero_count_t)(boost::winapi::HANDLE_ h); + +private: + auto_handle m_sem; + + static boost::atomic< is_semaphore_zero_count_t > is_semaphore_zero_count; + static nt_query_semaphore_t nt_query_semaphore; + +public: + void create_or_open(const wchar_t* name, permissions const& perms = permissions()); + void open(const wchar_t* name); + + boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_sem.get(); } + + void post(uint32_t count) + { + BOOST_ASSERT(count <= static_cast< uint32_t >((std::numeric_limits< boost::winapi::LONG_ >::max)())); + + if (BOOST_UNLIKELY(!boost::winapi::ReleaseSemaphore(m_sem.get(), static_cast< boost::winapi::LONG_ >(count), NULL))) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to post on an interprocess semaphore object", (err)); + } + } + + bool is_zero_count() const + { + return is_semaphore_zero_count.load(boost::memory_order_acquire)(m_sem.get()); + } + + void wait() + { + const boost::winapi::DWORD_ retval = boost::winapi::WaitForSingleObject(m_sem.get(), boost::winapi::infinite); + if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess semaphore object", (err)); + } + } + + bool wait(boost::winapi::HANDLE_ abort_handle) + { + boost::winapi::HANDLE_ handles[2u] = { m_sem.get(), abort_handle }; + const boost::winapi::DWORD_ retval = boost::winapi::WaitForMultipleObjects(2u, handles, false, boost::winapi::infinite); + if (retval == (boost::winapi::wait_object_0 + 1u)) + { + // Wait was interrupted + return false; + } + else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess semaphore object", (err)); + } + + return true; + } + + void swap(interprocess_semaphore& that) BOOST_NOEXCEPT + { + m_sem.swap(that.m_sem); + } + +private: + static bool is_semaphore_zero_count_init(boost::winapi::HANDLE_ h); + static bool is_semaphore_zero_count_nt_query_semaphore(boost::winapi::HANDLE_ h); + static bool is_semaphore_zero_count_emulated(boost::winapi::HANDLE_ h); +}; + +//! Interprocess mutex. Implementation adopted from Boost.Sync. +class interprocess_mutex +{ +public: + //! Shared state that should be visible to all processes using the mutex + struct shared_state + { + boost::atomic< uint32_t > m_lock_state; + + shared_state() BOOST_NOEXCEPT : m_lock_state(0u) + { + } + }; + + struct auto_unlock + { + explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {} + ~auto_unlock() { m_mutex.unlock(); } + + BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&)) + BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&)) + + private: + interprocess_mutex& m_mutex; + }; + + struct optional_unlock + { + optional_unlock() BOOST_NOEXCEPT : m_mutex(NULL) {} + explicit optional_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(&mutex) {} + ~optional_unlock() { if (m_mutex) m_mutex->unlock(); } + + interprocess_mutex* disengage() BOOST_NOEXCEPT + { + interprocess_mutex* p = m_mutex; + m_mutex = NULL; + return p; + } + + void engage(interprocess_mutex& mutex) BOOST_NOEXCEPT + { + BOOST_ASSERT(!m_mutex); + m_mutex = &mutex; + } + + BOOST_DELETED_FUNCTION(optional_unlock(optional_unlock const&)) + BOOST_DELETED_FUNCTION(optional_unlock& operator=(optional_unlock const&)) + + private: + interprocess_mutex* m_mutex; + }; + +private: + interprocess_event m_event; + shared_state* m_shared_state; + +#if !defined(BOOST_MSVC) || _MSC_VER >= 1800 + static BOOST_CONSTEXPR_OR_CONST uint32_t lock_flag_bit = 31u; + static BOOST_CONSTEXPR_OR_CONST uint32_t event_set_flag_bit = 30u; + static BOOST_CONSTEXPR_OR_CONST uint32_t lock_flag_value = 1u << lock_flag_bit; + static BOOST_CONSTEXPR_OR_CONST uint32_t event_set_flag_value = 1u << event_set_flag_bit; + static BOOST_CONSTEXPR_OR_CONST uint32_t waiter_count_mask = event_set_flag_value - 1u; +#else + // MSVC 8-11, inclusively, fail to link if these constants are declared as static constants instead of an enum + enum + { + lock_flag_bit = 31u, + event_set_flag_bit = 30u, + lock_flag_value = 1u << lock_flag_bit, + event_set_flag_value = 1u << event_set_flag_bit, + waiter_count_mask = event_set_flag_value - 1u + }; +#endif + +public: + interprocess_mutex() BOOST_NOEXCEPT : m_shared_state(NULL) + { + } + + void create(const wchar_t* name, shared_state* shared, permissions const& perms = permissions()) + { + m_event.create(name, false, perms); + m_shared_state = shared; + } + + void open(const wchar_t* name, shared_state* shared) + { + m_event.open(name); + m_shared_state = shared; + } + + bool try_lock() + { + return !bit_test_and_set(m_shared_state->m_lock_state, lock_flag_bit); + } + + void lock() + { + if (BOOST_UNLIKELY(!try_lock())) + lock_slow(); + } + + bool lock(boost::winapi::HANDLE_ abort_handle) + { + if (BOOST_LIKELY(try_lock())) + return true; + return lock_slow(abort_handle); + } + + void unlock() BOOST_NOEXCEPT + { + const uint32_t old_count = m_shared_state->m_lock_state.fetch_add(lock_flag_value, boost::memory_order_release); + if ((old_count & event_set_flag_value) == 0u && (old_count > lock_flag_value)) + { + if (!bit_test_and_set(m_shared_state->m_lock_state, event_set_flag_bit)) + { + m_event.set_noexcept(); + } + } + } + + BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&)) + BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&)) + +private: + void lock_slow(); + bool lock_slow(boost::winapi::HANDLE_ abort_handle); + void mark_waiting_and_try_lock(uint32_t& old_state); + void clear_waiting_and_try_lock(uint32_t& old_state); +}; + +//! A simple clock that corresponds to GetTickCount/GetTickCount64 timeline +struct tick_count_clock +{ +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + typedef boost::winapi::ULONGLONG_ time_point; +#else + typedef boost::winapi::DWORD_ time_point; +#endif + + static time_point now() BOOST_NOEXCEPT + { +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + return boost::winapi::GetTickCount64(); +#else + return boost::winapi::GetTickCount(); +#endif + } +}; + +//! Interprocess condition variable +class interprocess_condition_variable +{ +private: + typedef boost::intrusive::list_base_hook< + boost::intrusive::tag< struct for_sem_order_by_usage >, + boost::intrusive::link_mode< boost::intrusive::safe_link > + > semaphore_info_list_hook_t; + + typedef boost::intrusive::set_base_hook< + boost::intrusive::tag< struct for_sem_lookup_by_id >, + boost::intrusive::link_mode< boost::intrusive::safe_link >, + boost::intrusive::optimize_size< true > + > semaphore_info_set_hook_t; + + //! Information about a semaphore object + struct semaphore_info : + public semaphore_info_list_hook_t, + public semaphore_info_set_hook_t + { + struct order_by_id + { + typedef bool result_type; + + result_type operator() (semaphore_info const& left, semaphore_info const& right) const BOOST_NOEXCEPT + { + return left.m_id < right.m_id; + } + result_type operator() (semaphore_info const& left, uint32_t right) const BOOST_NOEXCEPT + { + return left.m_id < right; + } + result_type operator() (uint32_t left, semaphore_info const& right) const BOOST_NOEXCEPT + { + return left < right.m_id; + } + }; + + //! The semaphore + interprocess_semaphore m_semaphore; + //! Timestamp of the moment when the semaphore was checked for zero count and it was not zero. In milliseconds since epoch. + tick_count_clock::time_point m_last_check_for_zero; + //! The flag indicates that the semaphore has been checked for zero count and it was not zero + bool m_checked_for_zero; + //! The semaphore id + const uint32_t m_id; + + explicit semaphore_info(uint32_t id) BOOST_NOEXCEPT : m_last_check_for_zero(0u), m_id(id) + { + } + + //! Checks if the semaphore is in 'non-zero' state for too long + bool check_non_zero_timeout(tick_count_clock::time_point now) BOOST_NOEXCEPT + { + if (!m_checked_for_zero) + { + m_last_check_for_zero = now; + m_checked_for_zero = true; + return false; + } + + return (now - m_last_check_for_zero) >= 2000u; + } + + BOOST_DELETED_FUNCTION(semaphore_info(semaphore_info const&)) + BOOST_DELETED_FUNCTION(semaphore_info& operator=(semaphore_info const&)) + }; + + typedef boost::intrusive::list< + semaphore_info, + boost::intrusive::base_hook< semaphore_info_list_hook_t >, + boost::intrusive::constant_time_size< false > + > semaphore_info_list; + + typedef boost::intrusive::set< + semaphore_info, + boost::intrusive::base_hook< semaphore_info_set_hook_t >, + boost::intrusive::compare< semaphore_info::order_by_id >, + boost::intrusive::constant_time_size< false > + > semaphore_info_set; + +public: + struct shared_state + { + //! Number of waiters blocked on the semaphore if >0, 0 if no threads are blocked, <0 when the blocked threads were signalled + int32_t m_waiters; + //! The semaphore generation + uint32_t m_generation; + //! Id of the semaphore which is used to block threads on + uint32_t m_semaphore_id; + + shared_state() BOOST_NOEXCEPT : + m_waiters(0), + m_generation(0u), + m_semaphore_id(0u) + { + } + }; + +private: + //! The list of semaphores used for blocking. The list is in the order at which the semaphores are considered to be picked for being used. + semaphore_info_list m_semaphore_info_list; + //! The list of semaphores used for blocking. Used for searching for a particular semaphore by id. + semaphore_info_set m_semaphore_info_set; + //! The semaphore that is currently being used for blocking + semaphore_info* m_current_semaphore; + //! A string storage for formatting a semaphore name + std::wstring m_semaphore_name; + //! Permissions used to create new semaphores + permissions m_perms; + //! Process-shared state + shared_state* m_shared_state; + //! The next id for creating a new semaphore + uint32_t m_next_semaphore_id; + +public: + interprocess_condition_variable() BOOST_NOEXCEPT : + m_current_semaphore(NULL), + m_shared_state(NULL), + m_next_semaphore_id(0u) + { + } + + ~interprocess_condition_variable() + { + m_semaphore_info_set.clear(); + m_semaphore_info_list.clear_and_dispose(boost::checked_deleter< semaphore_info >()); + } + + void init(const wchar_t* name, shared_state* shared, permissions const& perms = permissions()) + { + m_perms = perms; + m_shared_state = shared; + + m_semaphore_name = name; + // Reserve space for generate_semaphore_name() + m_semaphore_name.append(L".sem00000000"); + + m_current_semaphore = get_semaphore(m_shared_state->m_semaphore_id); + } + + void notify_all() + { + const int32_t waiters = m_shared_state->m_waiters; + if (waiters > 0) + { + const uint32_t id = m_shared_state->m_semaphore_id; + if (m_current_semaphore->m_id != id) + m_current_semaphore = get_semaphore(id); + + m_current_semaphore->m_semaphore.post(waiters); + m_shared_state->m_waiters = -waiters; + } + } + + bool wait(interprocess_mutex::optional_unlock& lock, boost::winapi::HANDLE_ abort_handle); + + BOOST_DELETED_FUNCTION(interprocess_condition_variable(interprocess_condition_variable const&)) + BOOST_DELETED_FUNCTION(interprocess_condition_variable& operator=(interprocess_condition_variable const&)) + +private: + //! Finds or opens a semaphore with the specified id + semaphore_info* get_semaphore(uint32_t id); + //! Finds or creates a semaphore with zero counter + semaphore_info* get_unused_semaphore(); + + //! Marks the semaphore info as unused and moves to the end of list + void mark_unused(semaphore_info& info) BOOST_NOEXCEPT; + + //! Generates semaphore name according to id + void generate_semaphore_name(uint32_t id) BOOST_NOEXCEPT; + + //! Returns \c true if \a left is less than \a right considering possible integer overflow + static bool is_overflow_less(uint32_t left, uint32_t right) BOOST_NOEXCEPT + { + return ((left - right) & 0x80000000u) != 0u; + } +}; + +} // namespace aux + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/windows/light_rw_mutex.cpp b/src/boost/libs/log/src/windows/light_rw_mutex.cpp new file mode 100644 index 00000000..885dc175 --- /dev/null +++ b/src/boost/libs/log/src/windows/light_rw_mutex.cpp @@ -0,0 +1,200 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 light_rw_mutex.cpp + * \author Andrey Semashev + * \date 19.06.2010 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/log/detail/light_rw_mutex.hpp> + +#if !defined(BOOST_LOG_NO_THREADS) + +#if !defined(BOOST_LOG_LWRWMUTEX_USE_PTHREAD) && !defined(BOOST_LOG_LWRWMUTEX_USE_SRWLOCK) + +#include <cstddef> +#include <new> +#include <boost/assert.hpp> +#include <boost/align/aligned_alloc.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/log/utility/once_block.hpp> + +#include <boost/winapi/basic_types.hpp> +#include <boost/winapi/dll.hpp> + +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +struct BOOST_LOG_MAY_ALIAS mutex_impl { void* p; }; // has the same layout as SRWLOCK and light_rw_mutex::m_Mutex + +typedef void (BOOST_WINAPI_WINAPI_CC *init_fun_t)(mutex_impl*); +typedef void (BOOST_WINAPI_WINAPI_CC *destroy_fun_t)(mutex_impl*); +typedef void (BOOST_WINAPI_WINAPI_CC *lock_exclusive_fun_t)(mutex_impl*); +typedef void (BOOST_WINAPI_WINAPI_CC *lock_shared_fun_t)(mutex_impl*); +typedef void (BOOST_WINAPI_WINAPI_CC *unlock_exclusive_fun_t)(mutex_impl*); +typedef void (BOOST_WINAPI_WINAPI_CC *unlock_shared_fun_t)(mutex_impl*); + +//! A complement stub function for InitializeSRWLock +void BOOST_WINAPI_WINAPI_CC DeinitializeSRWLock(mutex_impl*) +{ +} + +// The Boost.Thread-based implementation +void BOOST_WINAPI_WINAPI_CC InitializeSharedMutex(mutex_impl* mtx) +{ + // To avoid cache line aliasing we do aligned memory allocation here + enum + { + // Allocation size is the minimum number of cache lines to accommodate shared_mutex + size = + ( + (sizeof(shared_mutex) + BOOST_LOG_CPU_CACHE_LINE_SIZE - 1u) / BOOST_LOG_CPU_CACHE_LINE_SIZE + ) + * BOOST_LOG_CPU_CACHE_LINE_SIZE + }; + mtx->p = alignment::aligned_alloc(BOOST_LOG_CPU_CACHE_LINE_SIZE, size); + BOOST_ASSERT(mtx->p != NULL); + new (mtx->p) shared_mutex(); +} + +void BOOST_WINAPI_WINAPI_CC DeinitializeSharedMutex(mutex_impl* mtx) +{ + static_cast< shared_mutex* >(mtx->p)->~shared_mutex(); + alignment::aligned_free(mtx->p); + mtx->p = NULL; +} + +void BOOST_WINAPI_WINAPI_CC ExclusiveLockSharedMutex(mutex_impl* mtx) +{ + static_cast< shared_mutex* >(mtx->p)->lock(); +} + +void BOOST_WINAPI_WINAPI_CC SharedLockSharedMutex(mutex_impl* mtx) +{ + static_cast< shared_mutex* >(mtx->p)->lock_shared(); +} + +void BOOST_WINAPI_WINAPI_CC ExclusiveUnlockSharedMutex(mutex_impl* mtx) +{ + static_cast< shared_mutex* >(mtx->p)->unlock(); +} + +void BOOST_WINAPI_WINAPI_CC SharedUnlockSharedMutex(mutex_impl* mtx) +{ + static_cast< shared_mutex* >(mtx->p)->unlock_shared(); +} + +// Pointers to the actual implementation functions +init_fun_t g_pInitializeLWRWMutex = NULL; +destroy_fun_t g_pDestroyLWRWMutex = NULL; +lock_exclusive_fun_t g_pLockExclusiveLWRWMutex = NULL; +lock_shared_fun_t g_pLockSharedLWRWMutex = NULL; +unlock_exclusive_fun_t g_pUnlockExclusiveLWRWMutex = NULL; +unlock_shared_fun_t g_pUnlockSharedLWRWMutex = NULL; + +//! The function dynamically initializes the implementation pointers +void init_light_rw_mutex_impl() +{ + boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); + if (hKernel32) + { + g_pInitializeLWRWMutex = + (init_fun_t)boost::winapi::get_proc_address(hKernel32, "InitializeSRWLock"); + if (g_pInitializeLWRWMutex) + { + g_pLockExclusiveLWRWMutex = + (lock_exclusive_fun_t)boost::winapi::get_proc_address(hKernel32, "AcquireSRWLockExclusive"); + if (g_pLockExclusiveLWRWMutex) + { + g_pUnlockExclusiveLWRWMutex = + (unlock_exclusive_fun_t)boost::winapi::get_proc_address(hKernel32, "ReleaseSRWLockExclusive"); + if (g_pUnlockExclusiveLWRWMutex) + { + g_pLockSharedLWRWMutex = + (lock_shared_fun_t)boost::winapi::get_proc_address(hKernel32, "AcquireSRWLockShared"); + if (g_pLockSharedLWRWMutex) + { + g_pUnlockSharedLWRWMutex = + (unlock_shared_fun_t)boost::winapi::get_proc_address(hKernel32, "ReleaseSRWLockShared"); + if (g_pUnlockSharedLWRWMutex) + { + g_pDestroyLWRWMutex = &DeinitializeSRWLock; + return; + } + } + } + } + } + } + + // Current OS doesn't have support for SRWLOCK, use Boost.Thread instead + g_pInitializeLWRWMutex = &InitializeSharedMutex; + g_pDestroyLWRWMutex = &DeinitializeSharedMutex; + g_pLockExclusiveLWRWMutex = &ExclusiveLockSharedMutex; + g_pUnlockExclusiveLWRWMutex = &ExclusiveUnlockSharedMutex; + g_pLockSharedLWRWMutex = &SharedLockSharedMutex; + g_pUnlockSharedLWRWMutex = &SharedUnlockSharedMutex; +} + +} // namespace + +BOOST_LOG_API light_rw_mutex::light_rw_mutex() +{ + BOOST_LOG_ONCE_BLOCK() + { + init_light_rw_mutex_impl(); + } + g_pInitializeLWRWMutex((mutex_impl*)&m_Mutex); +} + +BOOST_LOG_API light_rw_mutex::~light_rw_mutex() +{ + g_pDestroyLWRWMutex((mutex_impl*)&m_Mutex); +} + +BOOST_LOG_API void light_rw_mutex::lock_shared() +{ + g_pLockSharedLWRWMutex((mutex_impl*)&m_Mutex); +} + +BOOST_LOG_API void light_rw_mutex::unlock_shared() +{ + g_pUnlockSharedLWRWMutex((mutex_impl*)&m_Mutex); +} + +BOOST_LOG_API void light_rw_mutex::lock() +{ + g_pLockExclusiveLWRWMutex((mutex_impl*)&m_Mutex); +} + +BOOST_LOG_API void light_rw_mutex::unlock() +{ + g_pUnlockExclusiveLWRWMutex((mutex_impl*)&m_Mutex); +} + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // !defined(BOOST_LOG_LWRWMUTEX_USE_PTHREAD) && !defined(BOOST_LOG_LWRWMUTEX_USE_SRWLOCK) + +#endif // !defined(BOOST_LOG_NO_THREADS) diff --git a/src/boost/libs/log/src/windows/mapped_shared_memory.cpp b/src/boost/libs/log/src/windows/mapped_shared_memory.cpp new file mode 100644 index 00000000..4586c1d8 --- /dev/null +++ b/src/boost/libs/log/src/windows/mapped_shared_memory.cpp @@ -0,0 +1,251 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/mapped_shared_memory.cpp + * \author Andrey Semashev + * \date 13.02.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <boost/winapi/basic_types.hpp> +#include <boost/winapi/handles.hpp> +#include <boost/winapi/dll.hpp> +#include <boost/winapi/file_mapping.hpp> +#include <boost/winapi/page_protection_flags.hpp> +#include <boost/winapi/get_last_error.hpp> +#include <windows.h> // for error codes +#include <cstddef> +#include <limits> +#include <string> +#include <sstream> +#include <boost/assert.hpp> +#include <boost/cstdint.hpp> +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/throw_exception.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/permissions.hpp> +#include "windows/mapped_shared_memory.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +namespace aux { + +boost::atomic< mapped_shared_memory::nt_query_section_t > mapped_shared_memory::nt_query_section(static_cast< mapped_shared_memory::nt_query_section_t >(NULL)); + +mapped_shared_memory::~mapped_shared_memory() +{ + if (m_mapped_address) + unmap(); + + if (m_handle) + { + BOOST_VERIFY(boost::winapi::CloseHandle(m_handle) != 0); + m_handle = NULL; + } +} + +//! Creates a new file mapping for the shared memory segment or opens the existing one +void mapped_shared_memory::create(const wchar_t* name, std::size_t size, permissions const& perms) +{ + BOOST_ASSERT(m_handle == NULL); + + const uint64_t size64 = static_cast< uint64_t >(size); + + // Unlike other create functions, this function opens the existing mapping, if one already exists + boost::winapi::HANDLE_ h = boost::winapi::CreateFileMappingW + ( + boost::winapi::INVALID_HANDLE_VALUE_, + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + boost::winapi::PAGE_READWRITE_ | boost::winapi::SEC_COMMIT_, + static_cast< boost::winapi::DWORD_ >(size64 >> 32u), + static_cast< boost::winapi::DWORD_ >(size64), + name + ); + + boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + if (BOOST_UNLIKELY(h == NULL || err != ERROR_SUCCESS)) + { + if (h != NULL) + boost::winapi::CloseHandle(h); + std::ostringstream strm; + strm << "Failed to create a shared memory segment of " << size << " bytes"; + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, strm.str(), (err)); + } + + m_handle = h; + m_size = size; +} + +//! Creates a new file mapping for the shared memory segment or opens the existing one. Returns \c true if the region was created and \c false if an existing one was opened. +bool mapped_shared_memory::create_or_open(const wchar_t* name, std::size_t size, permissions const& perms) +{ + BOOST_ASSERT(m_handle == NULL); + + const uint64_t size64 = static_cast< uint64_t >(size); + + // Unlike other create functions, this function opens the existing mapping, if one already exists + boost::winapi::HANDLE_ h = boost::winapi::CreateFileMappingW + ( + boost::winapi::INVALID_HANDLE_VALUE_, + reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()), + boost::winapi::PAGE_READWRITE_ | boost::winapi::SEC_COMMIT_, + static_cast< boost::winapi::DWORD_ >(size64 >> 32u), + static_cast< boost::winapi::DWORD_ >(size64), + name + ); + + boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + if (BOOST_UNLIKELY(h == NULL)) + { + std::ostringstream strm; + strm << "Failed to create or open a shared memory segment of " << size << " bytes"; + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, strm.str(), (err)); + } + + const bool created = (err != ERROR_ALREADY_EXISTS); + try + { + if (created) + { + m_size = size; + } + else + { + // If an existing segment was opened, determine its size + m_size = obtain_size(h); + } + } + catch (...) + { + boost::winapi::CloseHandle(h); + throw; + } + + m_handle = h; + + return created; +} + +//! Opens the existing file mapping for the shared memory segment +void mapped_shared_memory::open(const wchar_t* name) +{ + BOOST_ASSERT(m_handle == NULL); + + // Note: FILE_MAP_WRITE implies reading permission as well + boost::winapi::HANDLE_ h = boost::winapi::OpenFileMappingW(boost::winapi::FILE_MAP_WRITE_ | boost::winapi::SECTION_QUERY_, false, name); + + if (BOOST_UNLIKELY(h == NULL)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to create a shared memory segment", (err)); + } + + try + { + m_size = obtain_size(h); + } + catch (...) + { + boost::winapi::CloseHandle(h); + throw; + } + + m_handle = h; +} + +//! Maps the file mapping into the current process memory +void mapped_shared_memory::map() +{ + BOOST_ASSERT(m_handle != NULL); + BOOST_ASSERT(m_mapped_address == NULL); + + // Note: FILE_MAP_WRITE implies reading permission as well + m_mapped_address = boost::winapi::MapViewOfFile + ( + m_handle, + boost::winapi::FILE_MAP_WRITE_ | boost::winapi::SECTION_QUERY_, + 0u, + 0u, + m_size + ); + + if (BOOST_UNLIKELY(m_mapped_address == NULL)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to map the shared memory segment into the process address space", (err)); + } +} + +//! Unmaps the file mapping +void mapped_shared_memory::unmap() +{ + BOOST_ASSERT(m_mapped_address != NULL); + + BOOST_VERIFY(boost::winapi::UnmapViewOfFile(m_mapped_address) != 0); + m_mapped_address = NULL; +} + +//! Returns the size of the file mapping identified by the handle +std::size_t mapped_shared_memory::obtain_size(boost::winapi::HANDLE_ h) +{ + nt_query_section_t query_section = nt_query_section.load(boost::memory_order_acquire); + + if (BOOST_UNLIKELY(query_section == NULL)) + { + // Check if ntdll.dll provides NtQuerySection, see: http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FSection%2FNtQuerySection.html + boost::winapi::HMODULE_ ntdll = boost::winapi::GetModuleHandleW(L"ntdll.dll"); + if (BOOST_UNLIKELY(ntdll == NULL)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to obtain a handle to ntdll.dll", (err)); + } + + query_section = (nt_query_section_t)boost::winapi::get_proc_address(ntdll, "NtQuerySection"); + if (BOOST_UNLIKELY(query_section == NULL)) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to obtain the NtQuerySection function", (err)); + } + + nt_query_section.store(query_section, boost::memory_order_release); + } + + section_basic_information info = {}; + boost::winapi::NTSTATUS_ err = query_section + ( + h, + 0u, // SectionBasicInformation + &info, + sizeof(info), + NULL + ); + if (BOOST_UNLIKELY(err != 0u)) + { + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to test obtain size of the shared memory segment", (ERROR_INVALID_HANDLE)); + } + + return static_cast< std::size_t >(info.section_size.QuadPart); +} + +} // namespace aux + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/windows/mapped_shared_memory.hpp b/src/boost/libs/log/src/windows/mapped_shared_memory.hpp new file mode 100644 index 00000000..1b5b85e5 --- /dev/null +++ b/src/boost/libs/log/src/windows/mapped_shared_memory.hpp @@ -0,0 +1,102 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/mapped_shared_memory.hpp + * \author Andrey Semashev + * \date 13.02.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WINDOWS_MAPPED_SHARED_MEMORY_HPP_INCLUDED_ +#define BOOST_LOG_WINDOWS_MAPPED_SHARED_MEMORY_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <boost/winapi/basic_types.hpp> +#include <cstddef> +#include <boost/assert.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/log/utility/permissions.hpp> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +namespace aux { + +/*! + * A replacement for to \c mapped_shared_memory and \c mapped_region from Boost.Interprocess. + * The significant difference is that the shared memory name is passed as a UTF-16 string and + * errors are reported as Boost.Log exceptions. + */ +class mapped_shared_memory +{ +private: + struct section_basic_information + { + void* base_address; + boost::winapi::ULONG_ section_attributes; + boost::winapi::LARGE_INTEGER_ section_size; + }; + typedef boost::winapi::NTSTATUS_ (__stdcall *nt_query_section_t)(boost::winapi::HANDLE_ h, unsigned int info_class, section_basic_information* pinfo, boost::winapi::ULONG_ info_size, boost::winapi::ULONG_* ret_len); + +private: + boost::winapi::HANDLE_ m_handle; + void* m_mapped_address; + std::size_t m_size; + static boost::atomic< nt_query_section_t > nt_query_section; + +public: + BOOST_CONSTEXPR mapped_shared_memory() BOOST_NOEXCEPT : + m_handle(NULL), + m_mapped_address(NULL), + m_size(0u) + { + } + + ~mapped_shared_memory(); + + //! Creates a new file mapping for the shared memory segment + void create(const wchar_t* name, std::size_t size, permissions const& perms = permissions()); + //! Creates a new file mapping for the shared memory segment or opens the existing one. Returns \c true if the region was created and \c false if an existing one was opened. + bool create_or_open(const wchar_t* name, std::size_t size, permissions const& perms = permissions()); + //! Opens the existing file mapping for the shared memory segment + void open(const wchar_t* name); + + //! Maps the file mapping into the current process memory + void map(); + //! Unmaps the file mapping + void unmap(); + + //! Returns the size of the opened shared memory segment + std::size_t size() const BOOST_NOEXCEPT { return m_size; } + //! Returns the address of the mapped shared memory + void* address() const BOOST_NOEXCEPT { return m_mapped_address; } + + BOOST_DELETED_FUNCTION(mapped_shared_memory(mapped_shared_memory const&)) + BOOST_DELETED_FUNCTION(mapped_shared_memory& operator=(mapped_shared_memory const&)) + +private: + //! Returns the size of the file mapping identified by the handle + static std::size_t obtain_size(boost::winapi::HANDLE_ h); +}; + +} // namespace aux + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WINDOWS_MAPPED_SHARED_MEMORY_HPP_INCLUDED_ diff --git a/src/boost/libs/log/src/windows/object_name.cpp b/src/boost/libs/log/src/windows/object_name.cpp new file mode 100644 index 00000000..5b56a035 --- /dev/null +++ b/src/boost/libs/log/src/windows/object_name.cpp @@ -0,0 +1,227 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/object_name.cpp + * \author Andrey Semashev + * \date 06.03.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <cstddef> +#include <cstdlib> +#include <string> +#include <vector> +#include <algorithm> +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/utility/ipc/object_name.hpp> +#include <boost/winapi/get_last_error.hpp> +#include <windows.h> +#include <lmcons.h> +#include <security.h> +#include "windows/auto_handle.hpp" +#include "windows/utf_code_conversion.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace ipc { + +BOOST_LOG_ANONYMOUS_NAMESPACE { + +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +class auto_boundary_descriptor +{ +private: + HANDLE m_handle; + +public: + explicit auto_boundary_descriptor(HANDLE h = NULL) BOOST_NOEXCEPT : m_handle(h) + { + } + + ~auto_boundary_descriptor() BOOST_NOEXCEPT + { + if (m_handle) + DeleteBoundaryDescriptor(m_handle); + } + + void init(HANDLE h) BOOST_NOEXCEPT + { + BOOST_ASSERT(m_handle == NULL); + m_handle = h; + } + + HANDLE get() const BOOST_NOEXCEPT { return m_handle; } + HANDLE* get_ptr() BOOST_NOEXCEPT { return &m_handle; } + + void swap(auto_boundary_descriptor& that) BOOST_NOEXCEPT + { + HANDLE h = m_handle; + m_handle = that.m_handle; + that.m_handle = h; + } + + BOOST_DELETED_FUNCTION(auto_boundary_descriptor(auto_boundary_descriptor const&)) + BOOST_DELETED_FUNCTION(auto_boundary_descriptor& operator=(auto_boundary_descriptor const&)) +}; + +//! Handle for the private namespace for \c user scope +static boost::atomic< HANDLE > g_user_private_namespace; + +//! Closes the private namespace on process exit +void close_user_namespace() +{ + HANDLE h = g_user_private_namespace.load(boost::memory_order_acquire); + if (h) + { + ClosePrivateNamespace(h, 0); + g_user_private_namespace.store((HANDLE)NULL, boost::memory_order_release); + } +} + +//! Attempts to create or open the private namespace +bool init_user_namespace() +{ + HANDLE h = g_user_private_namespace.load(boost::memory_order_acquire); + if (BOOST_UNLIKELY(!h)) + { + // Obtain the current user SID + boost::log::ipc::aux::auto_handle h_process_token; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, h_process_token.get_ptr())) + return false; + + DWORD token_user_size = 0; + GetTokenInformation(h_process_token.get(), TokenUser, NULL, 0u, &token_user_size); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || token_user_size < sizeof(TOKEN_USER)) + return false; + std::vector< unsigned char > token_user_storage(static_cast< std::size_t >(token_user_size), static_cast< unsigned char >(0u)); + if (!GetTokenInformation(h_process_token.get(), TokenUser, &token_user_storage[0], token_user_size, &token_user_size)) + return false; + + TOKEN_USER const& token_user = *reinterpret_cast< const TOKEN_USER* >(&token_user_storage[0]); + if (!token_user.User.Sid) + return false; + + // Create a boundary descriptor with the user's SID + auto_boundary_descriptor h_boundary(CreateBoundaryDescriptorW(L"User", 0)); + if (!h_boundary.get()) + return false; + + if (!AddSIDToBoundaryDescriptor(h_boundary.get_ptr(), token_user.User.Sid)) + return false; + + // Create or open a namespace for kernel objects + h = CreatePrivateNamespaceW(NULL, h_boundary.get(), L"User"); + if (!h) + h = OpenPrivateNamespaceW(h_boundary.get(), L"User"); + + if (h) + { + HANDLE expected = NULL; + if (g_user_private_namespace.compare_exchange_strong(expected, h, boost::memory_order_acq_rel, boost::memory_order_acquire)) + { + std::atexit(&close_user_namespace); + } + else + { + // Another thread must have opened the namespace + ClosePrivateNamespace(h, 0); + h = expected; + } + } + } + + return !!h; +} + +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +//! Returns a prefix string for a shared resource according to the scope +std::string get_scope_prefix(object_name::scope ns) +{ + std::string prefix; + switch (ns) + { + case object_name::process_group: + { + // For now consider all processes as members of the common process group. It may change if there is found + // a way to get a process group id (i.e. id of the closest parent process that was created with the CREATE_NEW_PROCESS_GROUP flag). + prefix = "Local\\boost.log.process_group"; + } + break; + + case object_name::session: + { + prefix = "Local\\boost.log.session"; + } + break; + + case object_name::user: + { +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + if (init_user_namespace()) + { + prefix = "User\\boost.log.user"; + } + else +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + { + wchar_t buf[UNLEN + 1u]; + ULONG len = sizeof(buf) / sizeof(*buf); + if (BOOST_UNLIKELY(!GetUserNameExW(NameSamCompatible, buf, &len))) + { + const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to obtain the current user name", (err)); + } + + std::replace(buf, buf + len, L'\\', L'.'); + + prefix = "Local\\boost.log.user." + boost::log::aux::utf16_to_utf8(buf); + } + } + break; + + default: + prefix = "Global\\boost.log.global"; + break; + } + + prefix.push_back('.'); + + return BOOST_LOG_NRVO_RESULT(prefix); +} + +} // namespace + +//! Constructor from the object name +BOOST_LOG_API object_name::object_name(scope ns, const char* str) : + m_name(get_scope_prefix(ns) + str) +{ +} + +//! Constructor from the object name +BOOST_LOG_API object_name::object_name(scope ns, std::string const& str) : + m_name(get_scope_prefix(ns) + str) +{ +} + +} // namespace ipc + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> diff --git a/src/boost/libs/log/src/windows/simple_event_log.mc b/src/boost/libs/log/src/windows/simple_event_log.mc new file mode 100644 index 00000000..f26eda9f --- /dev/null +++ b/src/boost/libs/log/src/windows/simple_event_log.mc @@ -0,0 +1,58 @@ +;/* +; * Copyright Andrey Semashev 2007 - 2015. +; * 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) +; * +; * This file is the Boost.Log library implementation, see the library documentation +; * at http://www.boost.org/libs/log/doc/log.html. +; */ +; +;/* -------------------------------------------------------- +; * HEADER SECTION +; */ +SeverityNames=(Debug=0x0:BOOST_LOG_SEVERITY_DEBUG + Info=0x1:BOOST_LOG_SEVERITY_INFO + Warning=0x2:BOOST_LOG_SEVERITY_WARNING + Error=0x3:BOOST_LOG_SEVERITY_ERROR + ) +; +; +; +;/* ------------------------------------------------------------------ +; * MESSAGE DEFINITION SECTION +; */ + +MessageIdTypedef=DWORD + +MessageId=0x100 +Severity=Debug +Facility=Application +SymbolicName=BOOST_LOG_MSG_DEBUG +Language=English +%1 +. + +MessageId=0x101 +Severity=Info +Facility=Application +SymbolicName=BOOST_LOG_MSG_INFO +Language=English +%1 +. + +MessageId=0x102 +Severity=Warning +Facility=Application +SymbolicName=BOOST_LOG_MSG_WARNING +Language=English +%1 +. + +MessageId=0x103 +Severity=Error +Facility=Application +SymbolicName=BOOST_LOG_MSG_ERROR +Language=English +%1 +. diff --git a/src/boost/libs/log/src/windows/utf_code_conversion.hpp b/src/boost/libs/log/src/windows/utf_code_conversion.hpp new file mode 100644 index 00000000..7b00f13d --- /dev/null +++ b/src/boost/libs/log/src/windows/utf_code_conversion.hpp @@ -0,0 +1,42 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 windows/utf_code_conversion.hpp + * \author Andrey Semashev + * \date 22.02.2016 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#ifndef BOOST_LOG_WINDOWS_UTF_CODE_CONVERSION_HPP_INCLUDED_ +#define BOOST_LOG_WINDOWS_UTF_CODE_CONVERSION_HPP_INCLUDED_ + +#include <boost/log/detail/config.hpp> +#include <string> +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Converts UTF-8 to UTF-16 +std::wstring utf8_to_utf16(const char* str); +//! Converts UTF-16 to UTF-8 +std::string utf16_to_utf8(const wchar_t* str); + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> + +#endif // BOOST_LOG_WINDOWS_UTF_CODE_CONVERSION_HPP_INCLUDED_ diff --git a/src/boost/libs/log/test/Jamfile.v2 b/src/boost/libs/log/test/Jamfile.v2 new file mode 100644 index 00000000..11246932 --- /dev/null +++ b/src/boost/libs/log/test/Jamfile.v2 @@ -0,0 +1,123 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# +# The file was adapted from libs/tr2/test/Jamfile.v2 by John Maddock. + +import testing ; +import path ; +import regex ; +import os ; +import ../build/log-platform-config ; + +project + : requirements + <conditional>@log-platform-config.set-platform-defines + + <include>common + + # Disable warnings about using 'insecure' standard C functions + <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS + <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS + <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE + <toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS + <toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE + + <toolset>msvc:<cxxflags>/bigobj + <toolset>msvc:<cxxflags>/wd4503 # decorated name length exceeded, name was truncated + <toolset>msvc:<cxxflags>/wd4456 # declaration of 'A' hides previous local declaration + <toolset>msvc:<cxxflags>/wd4459 # declaration of 'A' hides global declaration + <toolset>msvc:<cxxflags>/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + <toolset>msvc:<cxxflags>/wd4355 # 'this' : used in base member initializer list + + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + <toolset>intel-win:<cxxflags>"/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-linux:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + <toolset>intel-darwin:<cxxflags>"-wd177,780,2196,1782,193,304,981,1418,411,734,279" + + <toolset>darwin:<cxxflags>-ftemplate-depth-1024 + <toolset>gcc:<cxxflags>-ftemplate-depth-1024 + + <toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components + + # Boost.Interprocess does not compile on Cygwin: https://github.com/boostorg/interprocess/issues/76 + <target-os>cygwin:<define>BOOST_LOG_WITHOUT_IPC + + <library>/boost/log//boost_log + <library>/boost/log//boost_log_setup + <library>/boost/date_time//boost_date_time + <library>/boost/regex//boost_regex + <library>/boost/filesystem//boost_filesystem + <library>/boost/test//boost_unit_test_framework + <threading>single:<define>BOOST_LOG_NO_THREADS + <threading>multi:<library>/boost/thread//boost_thread + : default-build + # Testers typically don't specify threading environment and the library can be built and tested for single and multi. I'm more interested in multi though. + <threading>multi +# <link>static + ; + +# this rule enumerates through all the sources and invokes +# the run rule for each source, the result is a list of all +# the run rules, which we can pass on to the test_suite rule: +rule test_all +{ + local all_rules ; + local file ; + + if ! [ os.environ BOOST_LOG_TEST_WITHOUT_SELF_CONTAINED_HEADER_TESTS ] + { + local headers_path = [ path.make $(BOOST_ROOT)/libs/log/include/boost/log ] ; + for file in [ path.glob-tree $(headers_path) : *.hpp : detail ] + { + local rel_file = [ path.relative-to $(headers_path) $(file) ] ; + # Note: The test name starts with '~' in order to group these tests in the test report table, preferably at the end. + # All '/' are replaced with '-' because apparently test scripts have a problem with test names containing slashes. + local test_name = [ regex.replace ~hdr/$(rel_file) "/" "-" ] ; + #ECHO $(rel_file) ; + all_rules += [ compile compile/self_contained_header.cpp : <define>"BOOST_LOG_TEST_HEADER=$(rel_file)" <dependency>$(file) : $(test_name) ] ; + } + } + + for file in [ glob compile/*.cpp ] + { + if [ path.basename $(file) ] != "self_contained_header.cpp" + { + all_rules += [ compile $(file) ] ; + } + } + for file in [ glob compile_fail/*.cpp ] + { + all_rules += [ compile-fail $(file) ] ; + } + for file in [ glob run/*.cpp ] + { + all_rules += [ run $(file) ] ; + } + + if ! [ os.environ BOOST_LOG_TEST_WITHOUT_EXAMPLES ] + { + all_rules += [ build-project ../example ] ; + } + + #ECHO All rules: $(all_rules) ; + return $(all_rules) ; +} + +test-suite log : [ test_all ] ; diff --git a/src/boost/libs/log/test/common/attr_comparison.hpp b/src/boost/libs/log/test/common/attr_comparison.hpp new file mode 100644 index 00000000..b4be4f48 --- /dev/null +++ b/src/boost/libs/log/test/common/attr_comparison.hpp @@ -0,0 +1,60 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_comparison.hpp + * \author Andrey Semashev + * \date 06.08.2010 + * + * \brief This header contains tools for attribute comparison in attribute-related tests. + */ + +#ifndef BOOST_LOG_TESTS_ATTR_COMPARISON_HPP_INCLUDED_ +#define BOOST_LOG_TESTS_ATTR_COMPARISON_HPP_INCLUDED_ + +#include <ostream> +#include <boost/log/attributes/attribute.hpp> + +class attribute_factory_helper : + public boost::log::attribute +{ + typedef boost::log::attribute base_type; + +public: + attribute_factory_helper(base_type const& that) : base_type(that) + { + } + + impl* get_impl() const + { + return base_type::get_impl(); + } +}; + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +inline bool operator== (attribute const& left, attribute const& right) +{ + return attribute_factory_helper(left).get_impl() == attribute_factory_helper(right).get_impl(); +} +inline bool operator!= (attribute const& left, attribute const& right) +{ + return attribute_factory_helper(left).get_impl() != attribute_factory_helper(right).get_impl(); +} + +inline std::ostream& operator<< (std::ostream& strm, attribute const& val) +{ + strm << attribute_factory_helper(val).get_impl(); + return strm; +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // BOOST_LOG_TESTS_ATTR_COMPARISON_HPP_INCLUDED_ diff --git a/src/boost/libs/log/test/common/char_definitions.hpp b/src/boost/libs/log/test/common/char_definitions.hpp new file mode 100644 index 00000000..2cf0d664 --- /dev/null +++ b/src/boost/libs/log/test/common/char_definitions.hpp @@ -0,0 +1,116 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 char_definitions.hpp + * \author Andrey Semashev + * \date 24.01.2009 + * + * \brief This header contains common type definitions for character type dependent tests. + */ + +#ifndef BOOST_LOG_TESTS_CHAR_DEFINITIONS_HPP_INCLUDED_ +#define BOOST_LOG_TESTS_CHAR_DEFINITIONS_HPP_INCLUDED_ + +#include <string> +#include <iostream> +#include <boost/mpl/vector.hpp> + +namespace mpl = boost::mpl; + +typedef mpl::vector< +#ifdef BOOST_LOG_USE_CHAR + char +#endif +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + , +#endif +#ifdef BOOST_LOG_USE_WCHAR_T + wchar_t +#endif +>::type char_types; + +template< typename > +struct test_data; + + +#ifdef BOOST_LOG_USE_CHAR + +template< > +struct test_data< char > +{ + static const char* abc() { return "abc"; } + static const char* ABC() { return "ABC"; } + static const char* some_test_string() { return "some test string"; } + static const char* zero_to_five() { return "012345"; } + static const char* def() { return "def"; } + static const char* aaa() { return "aaa"; } + static const char* abcd() { return "abcd"; } + static const char* zz() { return "zz"; } + static const char* abcdefg0123456789() { return "abcdefg0123456789"; } + + static const char* attr1() { return "attr1"; } + static const char* attr2() { return "attr2"; } + static const char* attr3() { return "attr3"; } + static const char* attr4() { return "attr4"; } + + static const char* int_format1() { return "%08d"; } + static const char* fp_format1() { return "%06.3f"; } +}; + +//! The function compares two strings and prints them if they are not equal +inline bool equal_strings(std::string const& left, std::string const& right) +{ + if (left != right) + { + std::cout << "Left: \"" << left << "\"\nRight: \"" << right << "\"" << std::endl; + return false; + } + else + return true; +} + +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + +template< > +struct test_data< wchar_t > +{ + static const wchar_t* abc() { return L"abc"; } + static const wchar_t* ABC() { return L"ABC"; } + static const wchar_t* some_test_string() { return L"some test string"; } + static const wchar_t* zero_to_five() { return L"012345"; } + static const wchar_t* def() { return L"def"; } + static const wchar_t* aaa() { return L"aaa"; } + static const wchar_t* abcd() { return L"abcd"; } + static const wchar_t* zz() { return L"zz"; } + static const wchar_t* abcdefg0123456789() { return L"abcdefg0123456789"; } + + static const char* attr1() { return "attr1"; } + static const char* attr2() { return "attr2"; } + static const char* attr3() { return "attr3"; } + static const char* attr4() { return "attr4"; } + + static const wchar_t* int_format1() { return L"%08d"; } + static const wchar_t* fp_format1() { return L"%06.3f"; } +}; + +//! The function compares two strings and prints them if they are not equal +inline bool equal_strings(std::wstring const& left, std::wstring const& right) +{ + if (left != right) + { + std::wcout << L"Left: \"" << left << L"\"\nRight: \"" << right << L"\"" << std::endl; + return false; + } + else + return true; +} + +#endif // BOOST_LOG_USE_WCHAR_T + +#endif // BOOST_LOG_TESTS_CHAR_DEFINITIONS_HPP_INCLUDED_ diff --git a/src/boost/libs/log/test/common/make_record.hpp b/src/boost/libs/log/test/common/make_record.hpp new file mode 100644 index 00000000..9d1f028d --- /dev/null +++ b/src/boost/libs/log/test/common/make_record.hpp @@ -0,0 +1,32 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 make_record.hpp + * \author Andrey Semashev + * \date 18.03.2009 + * + * \brief This header contains a helper function make_record that creates a log record with the specified attributes. + */ + +#ifndef BOOST_LOG_TESTS_MAKE_RECORD_HPP_INCLUDED_ +#define BOOST_LOG_TESTS_MAKE_RECORD_HPP_INCLUDED_ + +#include <boost/move/utility_core.hpp> +#include <boost/log/core.hpp> +#include <boost/log/attributes/attribute_set.hpp> + +inline boost::log::record make_record(boost::log::attribute_set const& src_attrs = boost::log::attribute_set()) +{ + return boost::log::core::get()->open_record(src_attrs); +} + +inline boost::log::record_view make_record_view(boost::log::attribute_set const& src_attrs = boost::log::attribute_set()) +{ + return make_record(src_attrs).lock(); +} + +#endif // BOOST_LOG_TESTS_MAKE_RECORD_HPP_INCLUDED_ diff --git a/src/boost/libs/log/test/common/test_sink.hpp b/src/boost/libs/log/test/common/test_sink.hpp new file mode 100644 index 00000000..da88830e --- /dev/null +++ b/src/boost/libs/log/test/common/test_sink.hpp @@ -0,0 +1,92 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 test_sink.hpp + * \author Andrey Semashev + * \date 18.03.2009 + * + * \brief This header contains a test sink frontend that is used through various tests. + */ + +#ifndef BOOST_LOG_TESTS_TEST_SINK_HPP_INCLUDED_ +#define BOOST_LOG_TESTS_TEST_SINK_HPP_INCLUDED_ + +#include <cstddef> +#include <map> +#include <boost/log/core/record_view.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/sinks/sink.hpp> +#include <boost/log/expressions/filter.hpp> + +//! A sink implementation for testing purpose +struct test_sink : + public boost::log::sinks::sink +{ +public: + typedef boost::log::attribute_value_set attribute_values; + typedef boost::log::record_view record_type; + typedef boost::log::filter filter_type; + typedef attribute_values::key_type key_type; + + struct key_type_order + { + typedef bool result_type; + + result_type operator() (key_type const& left, key_type const& right) const + { + return left.id() < right.id(); + } + }; + + typedef std::map< key_type, std::size_t, key_type_order > attr_counters_map; + +public: + filter_type m_Filter; + attr_counters_map m_Consumed; + std::size_t m_RecordCounter; + +public: + test_sink() : boost::log::sinks::sink(false), m_RecordCounter(0) {} + + void set_filter(filter_type const& f) + { + m_Filter = f; + } + + void reset_filter() + { + m_Filter.reset(); + } + + bool will_consume(attribute_values const& attributes) + { + return m_Filter(attributes); + } + + void consume(record_type const& record) + { + ++m_RecordCounter; + attribute_values::const_iterator + it = record.attribute_values().begin(), + end = record.attribute_values().end(); + for (; it != end; ++it) + ++m_Consumed[it->first]; + } + + void flush() + { + } + + void clear() + { + m_RecordCounter = 0; + m_Consumed.clear(); + } +}; + +#endif // BOOST_LOG_TESTS_TEST_SINK_HPP_INCLUDED_ diff --git a/src/boost/libs/log/test/compile/current_function_support.cpp b/src/boost/libs/log/test/compile/current_function_support.cpp new file mode 100644 index 00000000..cdcd1051 --- /dev/null +++ b/src/boost/libs/log/test/compile/current_function_support.cpp @@ -0,0 +1,37 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 current_function_support.cpp + * \author Andrey Semashev + * \date 26.09.2010 + * + * \brief This test checks that the BOOST_CURRENT_FUNCTION macro has semantics + * compatible with Boost.Log on the current platform. + * + * The point of this test is to determine whether the macro unfolds into a string literal + * rather than a pointer to a string. This is critical because BOOST_LOG_WFUNCTION + * relies on this fact - it determines the length of the literal by applying sizeof to it. + */ + +#define BOOST_TEST_MODULE current_function_support + +#include <boost/current_function.hpp> +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_array.hpp> + +template< typename T > +void check(T& param) +{ + BOOST_STATIC_ASSERT(boost::is_array< T >::value); +} + +int main(int, char*[]) +{ + check(BOOST_CURRENT_FUNCTION); + + return 0; +} diff --git a/src/boost/libs/log/test/compile/self_contained_header.cpp b/src/boost/libs/log/test/compile/self_contained_header.cpp new file mode 100644 index 00000000..8e3a2c5a --- /dev/null +++ b/src/boost/libs/log/test/compile/self_contained_header.cpp @@ -0,0 +1,22 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 self_contained_header.cpp + * \author Andrey Semashev + * \date 15.03.2014 + * + * \brief This file contains a test boilerplate for checking that every public header is self-contained and does not have any missing #includes. + */ + +#define BOOST_LOG_TEST_INCLUDE_HEADER() <boost/log/BOOST_LOG_TEST_HEADER> + +#include BOOST_LOG_TEST_INCLUDE_HEADER() + +int main(int, char*[]) +{ + return 0; +} diff --git a/src/boost/libs/log/test/compile/src_logger_assignable.cpp b/src/boost/libs/log/test/compile/src_logger_assignable.cpp new file mode 100644 index 00000000..d1d51f57 --- /dev/null +++ b/src/boost/libs/log/test/compile/src_logger_assignable.cpp @@ -0,0 +1,39 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 src_logger_assignable.cpp + * \author Andrey Semashev + * \date 16.05.2011 + * + * \brief This header contains a test for logger assignability. + */ + +#include <boost/log/sources/logger.hpp> +#include <boost/log/sources/severity_logger.hpp> +#include <boost/log/sources/channel_logger.hpp> +#include <boost/log/sources/severity_channel_logger.hpp> + +template< typename LoggerT > +void test() +{ + LoggerT lg1, lg2; + + // Loggers must be assignable. The assignment operator must be taken + // from the composite_logger class and not auto-generated (in which + // case it will fail to compile because assignment in basic_logger is private). + lg1 = lg2; +} + +int main(int, char*[]) +{ + test< boost::log::sources::logger >(); + test< boost::log::sources::severity_logger< > >(); + test< boost::log::sources::channel_logger< > >(); + test< boost::log::sources::severity_channel_logger< > >(); + + return 0; +} diff --git a/src/boost/libs/log/test/compile/src_logger_get_attributes.cpp b/src/boost/libs/log/test/compile/src_logger_get_attributes.cpp new file mode 100644 index 00000000..b837917a --- /dev/null +++ b/src/boost/libs/log/test/compile/src_logger_get_attributes.cpp @@ -0,0 +1,44 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 src_logger_get_attributes.cpp + * \author Andrey Semashev + * \date 01.03.2014 + * + * \brief This header contains a test for logger \c get_attributes method. + */ + +#include <boost/log/sources/logger.hpp> +#include <boost/log/sources/severity_logger.hpp> +#include <boost/log/sources/channel_logger.hpp> +#include <boost/log/sources/severity_channel_logger.hpp> + +template< typename LoggerT > +void test() +{ + LoggerT lg; + + // Test that get_attributes(), which is a const method, can acquire the internal mutex in the threading model. + lg.get_attributes(); +} + +int main(int, char*[]) +{ + test< boost::log::sources::logger >(); + test< boost::log::sources::severity_logger< > >(); + test< boost::log::sources::channel_logger< > >(); + test< boost::log::sources::severity_channel_logger< > >(); + +#if !defined(BOOST_LOG_NO_THREADS) + test< boost::log::sources::logger_mt >(); + test< boost::log::sources::severity_logger_mt< > >(); + test< boost::log::sources::channel_logger_mt< > >(); + test< boost::log::sources::severity_channel_logger_mt< > >(); +#endif + + return 0; +} diff --git a/src/boost/libs/log/test/compile/util_unique_identifier.cpp b/src/boost/libs/log/test/compile/util_unique_identifier.cpp new file mode 100644 index 00000000..50e7ee03 --- /dev/null +++ b/src/boost/libs/log/test/compile/util_unique_identifier.cpp @@ -0,0 +1,34 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_unique_identifier.cpp + * \author Andrey Semashev + * \date 24.01.2009 + * + * \brief This header contains tests for the unique identifier name generator. + */ + +#include <boost/log/utility/unique_identifier_name.hpp> + +// Some hints to avoid warnings about unused variables in this test +#if defined(__GNUC__) +#define BOOST_LOG_AUX_UNUSED_ATTR __attribute__((unused)) +#else +#define BOOST_LOG_AUX_UNUSED_ATTR +#endif + +int main(int, char*[]) +{ + // Names with the same prefixes may coexist in different lines + BOOST_LOG_AUX_UNUSED_ATTR int BOOST_LOG_UNIQUE_IDENTIFIER_NAME(var) = 0; + BOOST_LOG_AUX_UNUSED_ATTR int BOOST_LOG_UNIQUE_IDENTIFIER_NAME(var) = 0; + + // Names with different prefixes may coexist on the same line + BOOST_LOG_AUX_UNUSED_ATTR int BOOST_LOG_UNIQUE_IDENTIFIER_NAME(var1) = 0; BOOST_LOG_AUX_UNUSED_ATTR int BOOST_LOG_UNIQUE_IDENTIFIER_NAME(var2) = 0; + + return 0; +} diff --git a/src/boost/libs/log/test/compile_fail/attr_functor_void_return.cpp b/src/boost/libs/log/test/compile_fail/attr_functor_void_return.cpp new file mode 100644 index 00000000..5ad7a599 --- /dev/null +++ b/src/boost/libs/log/test/compile_fail/attr_functor_void_return.cpp @@ -0,0 +1,42 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_functor_void_return.cpp + * \author Andrey Semashev + * \date 25.01.2009 + * + * \brief This test checks that it is not possible to create a functor attribute + * with a void-returning functor. + */ + +#define BOOST_TEST_MODULE attr_functor_void_return + +#include <boost/utility/result_of.hpp> +#include <boost/log/attributes/attribute.hpp> +#include <boost/log/attributes/function.hpp> + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + + // A test function that returns an attribute value + void get_attr_value() {} + +} // namespace + +int main(int, char*[]) +{ + logging::attribute attr1 = +#ifndef BOOST_NO_RESULT_OF + attrs::make_function(&get_attr_value); +#else + attrs::make_function< void >(&get_attr_value); +#endif + + return 0; +} diff --git a/src/boost/libs/log/test/performance/Jamfile.v2 b/src/boost/libs/log/test/performance/Jamfile.v2 new file mode 100644 index 00000000..036ed6e0 --- /dev/null +++ b/src/boost/libs/log/test/performance/Jamfile.v2 @@ -0,0 +1,15 @@ +# +# Copyright Andrey Semashev 2007 - 2015. +# 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) +# + +exe record_emission + : record_emission.cpp ../../build//boost_log + ; + +exe dump + : dump.cpp ../../build//boost_log + ; + diff --git a/src/boost/libs/log/test/performance/dump.cpp b/src/boost/libs/log/test/performance/dump.cpp new file mode 100644 index 00000000..b19b161a --- /dev/null +++ b/src/boost/libs/log/test/performance/dump.cpp @@ -0,0 +1,76 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 05.05.2013 + * + * \brief This code measures performance dumping binary data + */ + +#include <cstdlib> +#include <iomanip> +#include <string> +#include <vector> +#include <iostream> +#include <algorithm> + +#include <boost/cstdint.hpp> +#include <boost/date_time/microsec_time_clock.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/utility/manipulators/dump.hpp> + +namespace logging = boost::log; + +const unsigned int base_loop_count = 10000; + +void test(std::size_t block_size) +{ + std::cout << "Block size: " << block_size << " bytes."; + + std::vector< boost::uint8_t > data; + data.resize(block_size); + std::generate_n(data.begin(), block_size, &std::rand); + + std::string str; + logging::formatting_ostream strm(str); + + const boost::uint8_t* const p = &data[0]; + + boost::uint64_t data_processed = 0, duration = 0; + boost::posix_time::ptime start, end; + start = boost::date_time::microsec_clock< boost::posix_time::ptime >::universal_time(); + do + { + for (unsigned int i = 0; i < base_loop_count; ++i) + { + strm << logging::dump(p, block_size); + str.clear(); + } + end = boost::date_time::microsec_clock< boost::posix_time::ptime >::universal_time(); + data_processed += base_loop_count * block_size; + duration = (end - start).total_microseconds(); + } + while (duration < 2000000); + + std::cout << " Test duration: " << duration << " us (" + << std::fixed << std::setprecision(3) << static_cast< double >(data_processed) / (static_cast< double >(duration) * (1048576.0 / 1000000.0)) + << " MiB per second)" << std::endl; +} + +int main(int argc, char* argv[]) +{ + test(32); + test(128); + test(1024); + test(16384); + test(1048576); + + return 0; +} diff --git a/src/boost/libs/log/test/performance/record_emission.cpp b/src/boost/libs/log/test/performance/record_emission.cpp new file mode 100644 index 00000000..8657091c --- /dev/null +++ b/src/boost/libs/log/test/performance/record_emission.cpp @@ -0,0 +1,132 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 record_emission.cpp + * \author Andrey Semashev + * \date 22.03.2009 + * + * \brief This code measures performance of log record emission + */ + +// #define BOOST_LOG_USE_CHAR +// #define BOOST_ALL_DYN_LINK 1 +// #define BOOST_LOG_DYN_LINK 1 +#define BOOST_NO_DYN_LINK 1 + +#include <iomanip> +#include <iostream> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared_object.hpp> +#include <boost/date_time/microsec_time_clock.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/barrier.hpp> + +#include <boost/log/core.hpp> +#include <boost/log/common.hpp> +#include <boost/log/attributes.hpp> +#include <boost/log/sinks.hpp> +#include <boost/log/sinks/basic_sink_backend.hpp> +#include <boost/log/sources/logger.hpp> + +#include <boost/log/expressions.hpp> + +#include <boost/log/attributes/scoped_attribute.hpp> + +enum config +{ + RECORD_COUNT = 20000000, + THREAD_COUNT = 8, + SINK_COUNT = 3 +}; + +namespace logging = boost::log; +namespace expr = boost::log::expressions; +namespace sinks = boost::log::sinks; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace keywords = boost::log::keywords; + +enum severity_level +{ + normal, + warning, + error +}; + +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) + +namespace { + + //! A fake sink backend that receives log records + class fake_backend : + public sinks::basic_sink_backend< sinks::concurrent_feeding > + { + public: + void consume(logging::record_view const& rec) + { + } + }; + +} // namespace + +void test(unsigned int record_count, boost::barrier& bar) +{ + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + src::severity_logger< severity_level > slg; +// src::logger lg; + bar.wait(); + + for (unsigned int i = 0; i < record_count; ++i) + { + BOOST_LOG_SEV(slg, warning) << "Test record"; +// BOOST_LOG(lg) << "Test record"; + } +} + +int main(int argc, char* argv[]) +{ + std::cout << "Test config: " << THREAD_COUNT << " threads, " << SINK_COUNT << " sinks, " << RECORD_COUNT << " records" << std::endl; +//__debugbreak(); +// typedef sinks::unlocked_sink< fake_backend > fake_sink; +// typedef sinks::synchronous_sink< fake_backend > fake_sink; + typedef sinks::asynchronous_sink< fake_backend > fake_sink; + for (unsigned int i = 0; i < SINK_COUNT; ++i) + logging::core::get()->add_sink(boost::make_shared< fake_sink >()); + + logging::core::get()->add_global_attribute("LineID", attrs::counter< unsigned int >(1)); + logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock()); + logging::core::get()->add_global_attribute("Scope", attrs::named_scope()); + +// logging::core::get()->set_filter(severity > normal); // all records pass the filter +// logging::core::get()->set_filter(severity > error); // all records don't pass the filter + +// logging::core::get()->set_filter(severity > error); // all records don't pass the filter + + const unsigned int record_count = RECORD_COUNT / THREAD_COUNT; + boost::barrier bar(THREAD_COUNT); + boost::thread_group threads; + + for (unsigned int i = 1; i < THREAD_COUNT; ++i) + threads.create_thread(boost::bind(&test, record_count, boost::ref(bar))); + + boost::posix_time::ptime start = boost::date_time::microsec_clock< boost::posix_time::ptime >::universal_time(), end; + test(record_count, bar); + if (THREAD_COUNT > 1) + threads.join_all(); + end = boost::date_time::microsec_clock< boost::posix_time::ptime >::universal_time(); + + unsigned long long duration = (end - start).total_microseconds(); + + std::cout << "Test duration: " << duration << " us (" + << std::fixed << std::setprecision(3) << static_cast< double >(RECORD_COUNT) / (static_cast< double >(duration) / 1000000.0) + << " records per second)" << std::endl; + + return 0; +} diff --git a/src/boost/libs/log/test/run/attr_attribute_set.cpp b/src/boost/libs/log/test/run/attr_attribute_set.cpp new file mode 100644 index 00000000..669f4d09 --- /dev/null +++ b/src/boost/libs/log/test/run/attr_attribute_set.cpp @@ -0,0 +1,280 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_attribute_set.cpp + * \author Andrey Semashev + * \date 24.01.2009 + * + * \brief This header contains tests for the attribute set class. + */ + +#define BOOST_TEST_MODULE attr_attribute_set + +#include <list> +#include <vector> +#include <string> +#include <utility> +#include <iterator> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include "char_definitions.hpp" +#include "attr_comparison.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +// The test checks construction and assignment +BOOST_AUTO_TEST_CASE(construction) +{ + typedef logging::attribute_set attr_set; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1; + BOOST_CHECK(set1.empty()); + BOOST_CHECK_EQUAL(set1.size(), 0UL); + + attr_set set2 = set1; + BOOST_CHECK(set2.empty()); + BOOST_CHECK_EQUAL(set2.size(), 0UL); + + set2[data::attr1()] = attr1; + set2[data::attr2()] = attr2; + BOOST_CHECK(set1.empty()); + BOOST_CHECK_EQUAL(set1.size(), 0UL); + BOOST_CHECK(!set2.empty()); + BOOST_CHECK_EQUAL(set2.size(), 2UL); + + attr_set set3 = set2; + BOOST_CHECK(!set3.empty()); + BOOST_CHECK_EQUAL(set3.size(), 2UL); + BOOST_CHECK_EQUAL(set3.count(data::attr1()), 1UL); + BOOST_CHECK_EQUAL(set3.count(data::attr2()), 1UL); + BOOST_CHECK_EQUAL(set3.count(data::attr3()), 0UL); + + set1[data::attr3()] = attr3; + BOOST_CHECK(!set1.empty()); + BOOST_CHECK_EQUAL(set1.size(), 1UL); + BOOST_CHECK_EQUAL(set1.count(data::attr3()), 1UL); + + set2 = set1; + BOOST_REQUIRE_EQUAL(set1.size(), set2.size()); + BOOST_CHECK(std::equal(set1.begin(), set1.end(), set2.begin())); +} + +// The test checks lookup methods +BOOST_AUTO_TEST_CASE(lookup) +{ + typedef logging::attribute_set attr_set; + typedef test_data< char > data; + typedef std::basic_string< char > string; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + // Traditional find methods + attr_set::iterator it = set1.find(data::attr1()); + BOOST_CHECK(it != set1.end()); + BOOST_CHECK_EQUAL(it->second, attr1); + + string s1 = data::attr2(); + it = set1.find(s1); + BOOST_CHECK(it != set1.end()); + BOOST_CHECK_EQUAL(it->second, attr2); + + it = set1.find(data::attr1()); + BOOST_CHECK(it != set1.end()); + BOOST_CHECK_EQUAL(it->second, attr1); + + it = set1.find(data::attr3()); + BOOST_CHECK(it == set1.end()); + + // Subscript operator + logging::attribute p = set1[data::attr1()]; + BOOST_CHECK_EQUAL(p, attr1); + BOOST_CHECK_EQUAL(set1.size(), 2UL); + + p = set1[s1]; + BOOST_CHECK_EQUAL(p, attr2); + BOOST_CHECK_EQUAL(set1.size(), 2UL); + + p = set1[data::attr1()]; + BOOST_CHECK_EQUAL(p, attr1); + BOOST_CHECK_EQUAL(set1.size(), 2UL); + + p = set1[data::attr3()]; + BOOST_CHECK(!p); + BOOST_CHECK_EQUAL(set1.size(), 2UL); + + // Counting elements + BOOST_CHECK_EQUAL(set1.count(data::attr1()), 1UL); + BOOST_CHECK_EQUAL(set1.count(s1), 1UL); + BOOST_CHECK_EQUAL(set1.count(data::attr1()), 1UL); + BOOST_CHECK_EQUAL(set1.count(data::attr3()), 0UL); +} + +// The test checks insertion methods +BOOST_AUTO_TEST_CASE(insertion) +{ + typedef logging::attribute_set attr_set; + typedef test_data< char > data; + typedef std::basic_string< char > string; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1; + + // Traditional insert methods + std::pair< attr_set::iterator, bool > res = set1.insert(data::attr1(), attr1); + BOOST_CHECK(res.second); + BOOST_CHECK(res.first != set1.end()); + BOOST_CHECK(res.first->first == data::attr1()); + BOOST_CHECK_EQUAL(res.first->second, attr1); + BOOST_CHECK(!set1.empty()); + BOOST_CHECK_EQUAL(set1.size(), 1UL); + + res = set1.insert(std::make_pair(attr_set::key_type(data::attr2()), attr2)); + BOOST_CHECK(res.second); + BOOST_CHECK(res.first != set1.end()); + BOOST_CHECK(res.first->first == data::attr2()); + BOOST_CHECK_EQUAL(res.first->second, attr2); + BOOST_CHECK(!set1.empty()); + BOOST_CHECK_EQUAL(set1.size(), 2UL); + + // Insertion attempt of an attribute with the name of an already existing attribute + res = set1.insert(std::make_pair(attr_set::key_type(data::attr2()), attr3)); + BOOST_CHECK(!res.second); + BOOST_CHECK(res.first != set1.end()); + BOOST_CHECK(res.first->first == data::attr2()); + BOOST_CHECK_EQUAL(res.first->second, attr2); + BOOST_CHECK(!set1.empty()); + BOOST_CHECK_EQUAL(set1.size(), 2UL); + + // Mass insertion + typedef attr_set::key_type key_type; + std::list< std::pair< key_type, logging::attribute > > elems; + elems.push_back(std::make_pair(key_type(data::attr2()), attr2)); + elems.push_back(std::make_pair(key_type(data::attr1()), attr1)); + elems.push_back(std::make_pair(key_type(data::attr3()), attr3)); + // ... with element duplication + elems.push_back(std::make_pair(key_type(data::attr1()), attr3)); + + attr_set set2; + set2.insert(elems.begin(), elems.end()); + BOOST_CHECK(!set2.empty()); + BOOST_REQUIRE_EQUAL(set2.size(), 3UL); + typedef attr_set::mapped_type mapped_type; + BOOST_CHECK_EQUAL(static_cast< mapped_type >(set2[data::attr1()]), attr1); + BOOST_CHECK_EQUAL(static_cast< mapped_type >(set2[data::attr2()]), attr2); + BOOST_CHECK_EQUAL(static_cast< mapped_type >(set2[data::attr3()]), attr3); + + // The same, but with insertion results collection + std::vector< std::pair< attr_set::iterator, bool > > results; + + attr_set set3; + set3.insert(elems.begin(), elems.end(), std::back_inserter(results)); + BOOST_REQUIRE_EQUAL(results.size(), elems.size()); + BOOST_CHECK(!set3.empty()); + BOOST_REQUIRE_EQUAL(set3.size(), 3UL); + attr_set::iterator it = set3.find(data::attr1()); + BOOST_REQUIRE(it != set3.end()); + BOOST_CHECK(it->first == data::attr1()); + BOOST_CHECK_EQUAL(it->second, attr1); + BOOST_CHECK(it == results[1].first); + it = set3.find(data::attr2()); + BOOST_REQUIRE(it != set3.end()); + BOOST_CHECK(it->first == data::attr2()); + BOOST_CHECK_EQUAL(it->second, attr2); + BOOST_CHECK(it == results[0].first); + it = set3.find(data::attr3()); + BOOST_REQUIRE(it != set3.end()); + BOOST_CHECK(it->first == data::attr3()); + BOOST_CHECK_EQUAL(it->second, attr3); + BOOST_CHECK(it == results[2].first); + + BOOST_CHECK(results[0].second); + BOOST_CHECK(results[1].second); + BOOST_CHECK(results[2].second); + BOOST_CHECK(!results[3].second); + + // Subscript operator + attr_set set4; + + logging::attribute& p1 = (set4[data::attr1()] = attr1); + BOOST_CHECK_EQUAL(set4.size(), 1UL); + BOOST_CHECK_EQUAL(p1, attr1); + + logging::attribute& p2 = (set4[string(data::attr2())] = attr2); + BOOST_CHECK_EQUAL(set4.size(), 2UL); + BOOST_CHECK_EQUAL(p2, attr2); + + logging::attribute& p3 = (set4[key_type(data::attr3())] = attr3); + BOOST_CHECK_EQUAL(set4.size(), 3UL); + BOOST_CHECK_EQUAL(p3, attr3); + + // subscript operator can replace existing elements + logging::attribute& p4 = (set4[data::attr3()] = attr1); + BOOST_CHECK_EQUAL(set4.size(), 3UL); + BOOST_CHECK_EQUAL(p4, attr1); +} + +// The test checks erasure methods +BOOST_AUTO_TEST_CASE(erasure) +{ + typedef logging::attribute_set attr_set; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_set set2 = set1; + BOOST_REQUIRE_EQUAL(set2.size(), 3UL); + + BOOST_CHECK_EQUAL(set2.erase(data::attr1()), 1UL); + BOOST_CHECK_EQUAL(set2.size(), 2UL); + BOOST_CHECK_EQUAL(set2.count(data::attr1()), 0UL); + + BOOST_CHECK_EQUAL(set2.erase(data::attr1()), 0UL); + BOOST_CHECK_EQUAL(set2.size(), 2UL); + + set2.erase(set2.begin()); + BOOST_CHECK_EQUAL(set2.size(), 1UL); + BOOST_CHECK_EQUAL(set2.count(data::attr2()), 0UL); + + set2 = set1; + BOOST_REQUIRE_EQUAL(set2.size(), 3UL); + + attr_set::iterator it = set2.begin(); + set2.erase(++it, set2.end()); + BOOST_CHECK_EQUAL(set2.size(), 1UL); + BOOST_CHECK_EQUAL(set2.count(data::attr1()), 1UL); + BOOST_CHECK_EQUAL(set2.count(data::attr2()), 0UL); + BOOST_CHECK_EQUAL(set2.count(data::attr3()), 0UL); + + set2 = set1; + BOOST_REQUIRE_EQUAL(set2.size(), 3UL); + + set2.clear(); + BOOST_CHECK(set2.empty()); + BOOST_CHECK_EQUAL(set2.size(), 0UL); +} diff --git a/src/boost/libs/log/test/run/attr_attribute_set_ticket11106.cpp b/src/boost/libs/log/test/run/attr_attribute_set_ticket11106.cpp new file mode 100644 index 00000000..142307d9 --- /dev/null +++ b/src/boost/libs/log/test/run/attr_attribute_set_ticket11106.cpp @@ -0,0 +1,51 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_attribute_set_ticket11106.cpp + * \author Andrey Semashev + * \date 15.03.2015 + * + * \brief This header contains a test for the fix for https://svn.boost.org/trac/boost/ticket/11106. + */ + +#define BOOST_TEST_MODULE attr_attribute_set_ticket11106 + +#include <string> +#include <sstream> +#include <utility> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> + +// The test checks that insertion does not invalidate existing elements in the container +BOOST_AUTO_TEST_CASE(ticket11106) +{ + boost::log::attribute_set set; + + unsigned int i = 0; + while (i < 100) + { + std::ostringstream strm; + strm << "Attr" << i; + boost::log::attribute_name name = strm.str(); + + std::pair< boost::log::attribute_set::iterator, bool > res = set.insert(name, boost::log::attributes::make_constant(5)); + ++i; + + BOOST_CHECK(res.second); // check that insertion succeeded + BOOST_CHECK(set.find(res.first->first) != set.end()); // check that lookup works + + // Now check that all previously inserted elements are still findable + unsigned int j = 0; + for (boost::log::attribute_set::const_iterator it = set.begin(), end = set.end(); it != end; ++it, ++j) + { + boost::log::attribute_name key = it->first; + BOOST_CHECK(set.find(key) != set.end()); + } + BOOST_CHECK_EQUAL(j, i); + } +} diff --git a/src/boost/libs/log/test/run/attr_attribute_value_impl.cpp b/src/boost/libs/log/test/run/attr_attribute_value_impl.cpp new file mode 100644 index 00000000..682b2e48 --- /dev/null +++ b/src/boost/libs/log/test/run/attr_attribute_value_impl.cpp @@ -0,0 +1,153 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_attribute_value_impl.cpp + * \author Andrey Semashev + * \date 25.01.2009 + * + * \brief This header contains tests for the basic attribute value class. + */ + +#define BOOST_TEST_MODULE attr_attribute_value_impl + +#include <string> +#include <boost/smart_ptr/intrusive_ptr.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/tools//floating_point_comparison.hpp> +#include <boost/log/attributes/attribute_value.hpp> +#include <boost/log/attributes/attribute_value_impl.hpp> +#include <boost/log/attributes/value_extraction.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/utility/type_dispatch/static_type_dispatcher.hpp> +#include <boost/log/utility/functional/bind_assign.hpp> + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + + // Type dispatcher for the supported types + struct my_dispatcher : + public logging::static_type_dispatcher< + boost::mpl::vector< int, double, std::string > + > + { + typedef logging::static_type_dispatcher< + boost::mpl::vector< int, double, std::string > + > base_type; + + enum type_expected + { + none_expected, + int_expected, + double_expected, + string_expected + }; + + my_dispatcher() : + base_type(*this), + m_Expected(none_expected), + m_Int(0), + m_Double(0.0) + { + } + + void set_expected() + { + m_Expected = none_expected; + } + void set_expected(int value) + { + m_Expected = int_expected; + m_Int = value; + } + void set_expected(double value) + { + m_Expected = double_expected; + m_Double = value; + } + void set_expected(std::string const& value) + { + m_Expected = string_expected; + m_String = value; + } + + // Implement visitation logic for all supported types + void operator() (int const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, int_expected); + BOOST_CHECK_EQUAL(m_Int, value); + } + void operator() (double const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, double_expected); + BOOST_CHECK_CLOSE(m_Double, value, 0.001); + } + void operator() (std::string const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, string_expected); + BOOST_CHECK_EQUAL(m_String, value); + } + + private: + type_expected m_Expected; + int m_Int; + double m_Double; + std::string m_String; + }; + +} // namespace + +// The test verifies that type dispatching works +BOOST_AUTO_TEST_CASE(type_dispatching) +{ + my_dispatcher disp; + logging::attribute_value p1(attrs::make_attribute_value< int >(10)); + logging::attribute_value p2(attrs::make_attribute_value< double > (5.5)); + logging::attribute_value p3(attrs::make_attribute_value< std::string >(std::string("Hello, world!"))); + logging::attribute_value p4(attrs::make_attribute_value< float >(static_cast< float >(-7.2))); + + disp.set_expected(10); + BOOST_CHECK(p1.dispatch(disp)); + BOOST_CHECK(p1.dispatch(disp)); // check that the contained value doesn't change over time or upon dispatching + + disp.set_expected(5.5); + BOOST_CHECK(p2.dispatch(disp)); + + disp.set_expected("Hello, world!"); + BOOST_CHECK(p3.dispatch(disp)); + + disp.set_expected(); + BOOST_CHECK(!p4.dispatch(disp)); +} + +// The test verifies that value extraction works +BOOST_AUTO_TEST_CASE(value_extraction) +{ + logging::attribute_value p1(attrs::make_attribute_value< int >(10)); + logging::attribute_value p2(attrs::make_attribute_value< double >(5.5)); + + logging::value_ref< int > val1 = p1.extract< int >(); + BOOST_CHECK(!!val1); + BOOST_CHECK_EQUAL(val1.get(), 10); + + logging::value_ref< double > val2 = p1.extract< double >(); + BOOST_CHECK(!val2); + + double val3 = 0.0; + BOOST_CHECK(p2.visit< double >(logging::bind_assign(val3))); + BOOST_CHECK_CLOSE(val3, 5.5, 0.001); +} + +// The test verifies that the detach_from_thread returns a valid pointer +BOOST_AUTO_TEST_CASE(detaching_from_thread) +{ + boost::intrusive_ptr< logging::attribute_value::impl > p1(new attrs::attribute_value_impl< int >(10)); + boost::intrusive_ptr< logging::attribute_value::impl > p2 = p1->detach_from_thread(); + BOOST_CHECK(!!p2); +} diff --git a/src/boost/libs/log/test/run/attr_attribute_value_set.cpp b/src/boost/libs/log/test/run/attr_attribute_value_set.cpp new file mode 100644 index 00000000..1b806738 --- /dev/null +++ b/src/boost/libs/log/test/run/attr_attribute_value_set.cpp @@ -0,0 +1,244 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_attribute_value_set.cpp + * \author Andrey Semashev + * \date 24.01.2009 + * + * \brief This header contains tests for the attribute value set. + */ + +#define BOOST_TEST_MODULE attr_attribute_value_set + +#include <vector> +#include <string> +#include <sstream> +#include <utility> +#include <iterator> +#include <boost/config.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/tools//floating_point_comparison.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/utility/type_dispatch/static_type_dispatcher.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + + //! A simple attribute value receiver functional object + template< typename T > + struct receiver + { + typedef void result_type; + receiver(T& val) : m_Val(val) {} + result_type operator() (T const& val) const + { + m_Val = val; + } + + private: + T& m_Val; + }; + + //! The function extracts attribute value + template< typename T > + inline bool get_attr_value(logging::attribute_value const& val, T& res) + { + receiver< T > r(res); + logging::static_type_dispatcher< T > disp(r); + return val.dispatch(disp); + } + +} // namespace + +// The test checks construction and assignment +BOOST_AUTO_TEST_CASE(construction) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + attrs::constant< char > attr4('L'); + + { + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values view1(set1, set2, set3); + view1.freeze(); + + BOOST_CHECK(!view1.empty()); + BOOST_CHECK_EQUAL(view1.size(), 3UL); + } + { + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set2[data::attr2()] = attr2; + set3[data::attr3()] = attr3; + + attr_values view1(set1, set2, set3); + view1.freeze(); + + BOOST_CHECK(!view1.empty()); + BOOST_CHECK_EQUAL(view1.size(), 3UL); + + attr_values view2 = view1; + BOOST_CHECK(!view2.empty()); + BOOST_CHECK_EQUAL(view2.size(), 3UL); + } + + // Check that the more prioritized attributes replace the less ones + { + attrs::constant< int > attr2_2(20); + attrs::constant< double > attr4_2(10.3); + attrs::constant< float > attr3_3(static_cast< float >(-7.2)); + attrs::constant< unsigned int > attr4_3(5); + + attr_set set1, set2, set3; + set3[data::attr1()] = attr1; + set3[data::attr2()] = attr2; + set3[data::attr3()] = attr3; + set3[data::attr4()] = attr4; + + set2[data::attr2()] = attr2_2; + set2[data::attr4()] = attr4_2; + + set1[data::attr3()] = attr3_3; + set1[data::attr4()] = attr4_3; + + attr_values view1(set1, set2, set3); + view1.freeze(); + + BOOST_CHECK(!view1.empty()); + BOOST_CHECK_EQUAL(view1.size(), 4UL); + + int n = 0; + BOOST_CHECK(logging::visit< int >(data::attr1(), view1, receiver< int >(n))); + BOOST_CHECK_EQUAL(n, 10); + + BOOST_CHECK(logging::visit< int >(data::attr2(), view1, receiver< int >(n))); + BOOST_CHECK_EQUAL(n, 20); + + float f = static_cast< float >(0.0); + BOOST_CHECK(logging::visit< float >(data::attr3(), view1, receiver< float >(f))); + BOOST_CHECK_CLOSE(f, static_cast< float >(-7.2), static_cast< float >(0.001)); + + unsigned int m = 0; + BOOST_CHECK(logging::visit< unsigned int >(data::attr4(), view1, receiver< unsigned int >(m))); + BOOST_CHECK_EQUAL(m, 5U); + } +} + +// The test checks lookup methods +BOOST_AUTO_TEST_CASE(lookup) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef test_data< char > data; + typedef std::basic_string< char > string; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values view1(set1, set2, set3); + view1.freeze(); + + // Traditional find methods + attr_values::const_iterator it = view1.find(data::attr1()); + BOOST_CHECK(it != view1.end()); + BOOST_CHECK(it->first == data::attr1()); + int val1 = 0; + BOOST_CHECK(get_attr_value(it->second, val1)); + BOOST_CHECK_EQUAL(val1, 10); + + string s1 = data::attr2(); + it = view1.find(s1); + BOOST_CHECK(it != view1.end()); + BOOST_CHECK(it->first == data::attr2()); + double val2 = 0; + BOOST_CHECK(get_attr_value(it->second, val2)); + BOOST_CHECK_CLOSE(val2, 5.5, 0.001); + + it = view1.find(data::attr3()); + BOOST_CHECK(it != view1.end()); + BOOST_CHECK(it->first == data::attr3()); + std::string val3; + BOOST_CHECK(get_attr_value(it->second, val3)); + BOOST_CHECK_EQUAL(val3, "Hello, world!"); + + // make an additional check that the result is absent if the value type does not match the requested type + BOOST_CHECK(!get_attr_value(it->second, val2)); + + it = view1.find(data::attr4()); + BOOST_CHECK(it == view1.end()); + + // Subscript operator + logging::attribute_value p = view1[data::attr1()]; + BOOST_CHECK_EQUAL(view1.size(), 3UL); + BOOST_CHECK(!!p); + BOOST_CHECK(get_attr_value(p, val1)); + BOOST_CHECK_EQUAL(val1, 10); + + p = view1[s1]; + BOOST_CHECK_EQUAL(view1.size(), 3UL); + BOOST_CHECK(!!p); + BOOST_CHECK(get_attr_value(p, val2)); + BOOST_CHECK_CLOSE(val2, 5.5, 0.001); + + p = view1[data::attr3()]; + BOOST_CHECK_EQUAL(view1.size(), 3UL); + BOOST_CHECK(!!p); + BOOST_CHECK(get_attr_value(p, val3)); + BOOST_CHECK_EQUAL(val3, "Hello, world!"); + + p = view1[data::attr4()]; + BOOST_CHECK(!p); + BOOST_CHECK_EQUAL(view1.size(), 3UL); + + // Counting elements + BOOST_CHECK_EQUAL(view1.count(data::attr1()), 1UL); + BOOST_CHECK_EQUAL(view1.count(s1), 1UL); + BOOST_CHECK_EQUAL(view1.count(data::attr3()), 1UL); + BOOST_CHECK_EQUAL(view1.count(data::attr4()), 0UL); +} + +// The test checks size method +BOOST_AUTO_TEST_CASE(size) +{ + typedef logging::attribute_value_set attr_values; + attrs::constant< int > attr1(10); + + attr_values view; + view.freeze(); + + unsigned int i = 0; + for (; i < 100; ++i) + { + std::ostringstream strm; + strm << "Attr" << i; + + view.insert(attr_values::key_type(strm.str()), attr1.get_value()); + } + + BOOST_CHECK_EQUAL(view.size(), i); +} diff --git a/src/boost/libs/log/test/run/attr_attribute_value_set_ticket11190.cpp b/src/boost/libs/log/test/run/attr_attribute_value_set_ticket11190.cpp new file mode 100644 index 00000000..d5a0388f --- /dev/null +++ b/src/boost/libs/log/test/run/attr_attribute_value_set_ticket11190.cpp @@ -0,0 +1,60 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_attribute_value_set_ticket11190.cpp + * \author Andrey Semashev + * \date 25.04.2015 + * + * \brief This header contains a test for the fix for https://svn.boost.org/trac/boost/ticket/11190. + */ + +#define BOOST_TEST_MODULE attr_attribute_value_set_ticket11190 + +#include <string> +#include <sstream> +#include <utility> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> + +// The test checks that insertion does not invalidate existing elements in the container +BOOST_AUTO_TEST_CASE(ticket11190) +{ + boost::log::attribute_set set, dummy; + + unsigned int i = 0; + while (i < 100) + { + std::ostringstream strm; + strm << "Attr" << i; + boost::log::attribute_name name = strm.str(); + + std::pair< boost::log::attribute_set::iterator, bool > res = set.insert(name, boost::log::attributes::make_constant(5)); + ++i; + + BOOST_CHECK(res.second); // check that insertion succeeded + // check that lookup works + boost::log::attribute_set::iterator find_result = set.find(name); + BOOST_CHECK(find_result != set.end()); + BOOST_CHECK(find_result == res.first); + } + + boost::log::attribute_value_set vset(set, dummy, dummy); + BOOST_CHECK_EQUAL(vset.size(), set.size()); + + // Check that all inserted elements are findable + unsigned int j = 0; + for (boost::log::attribute_value_set::const_iterator it = vset.begin(), end = vset.end(); it != end; ++it, ++j) + { + boost::log::attribute_name key = it->first; + BOOST_CHECK(vset.find(key) != vset.end()); + } + + // Check that vset.size() is valid + BOOST_CHECK_EQUAL(j, i); +} diff --git a/src/boost/libs/log/test/run/attr_function.cpp b/src/boost/libs/log/test/run/attr_function.cpp new file mode 100644 index 00000000..014097f7 --- /dev/null +++ b/src/boost/libs/log/test/run/attr_function.cpp @@ -0,0 +1,138 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_function.cpp + * \author Andrey Semashev + * \date 25.01.2009 + * + * \brief This header contains tests for the function attribute. + */ + +#define BOOST_TEST_MODULE attr_function + +#include <string> +#include <boost/mpl/vector.hpp> +#include <boost/utility/result_of.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/function.hpp> +#include <boost/log/utility/type_dispatch/static_type_dispatcher.hpp> + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + + // Type dispatcher for the supported types + struct my_dispatcher : + public logging::static_type_dispatcher< + boost::mpl::vector< int, std::string > + > + { + typedef logging::static_type_dispatcher< + boost::mpl::vector< int, std::string > + > base_type; + + enum type_expected + { + none_expected, + int_expected, + string_expected + }; + + my_dispatcher() : base_type(*this), m_Expected(none_expected), m_Int(0) {} + + void set_expected() + { + m_Expected = none_expected; + } + void set_expected(int value) + { + m_Expected = int_expected; + m_Int = value; + } + void set_expected(std::string const& value) + { + m_Expected = string_expected; + m_String = value; + } + + // Implement visitation logic for all supported types + void operator() (int const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, int_expected); + BOOST_CHECK_EQUAL(m_Int, value); + } + void operator() (std::string const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, string_expected); + BOOST_CHECK_EQUAL(m_String, value); + } + + private: + type_expected m_Expected; + int m_Int; + std::string m_String; + }; + + // A test function that returns an attribute value + int get_attr_value() + { + return 10; + } + + // A test functional object that returns an attribute value + struct attr_value_generator + { + typedef std::string result_type; + + explicit attr_value_generator(unsigned int& count) : m_CallsCount(count) {} + result_type operator() () const + { + ++m_CallsCount; + return "Hello, world!"; + } + + private: + unsigned int& m_CallsCount; + }; + +} // namespace + +// The test verifies that the attribute calls the specified functor and returns the value returned by the functor +BOOST_AUTO_TEST_CASE(calling) +{ + unsigned int call_count = 0; + my_dispatcher disp; + + logging::attribute attr1 = +#ifndef BOOST_NO_RESULT_OF + attrs::make_function(&get_attr_value); +#else + attrs::make_function< int >(&get_attr_value); +#endif + + logging::attribute attr2 = +#ifndef BOOST_NO_RESULT_OF + attrs::make_function(attr_value_generator(call_count)); +#else + attrs::make_function< std::string >(attr_value_generator(call_count)); +#endif + + logging::attribute_value p1(attr1.get_value()); + logging::attribute_value p2(attr2.get_value()); + BOOST_CHECK_EQUAL(call_count, 1u); + logging::attribute_value p3(attr2.get_value()); + BOOST_CHECK_EQUAL(call_count, 2u); + + disp.set_expected(10); + BOOST_CHECK(p1.dispatch(disp)); + BOOST_CHECK(p1.dispatch(disp)); // check that the contained value doesn't change over time or upon dispatching + + disp.set_expected("Hello, world!"); + BOOST_CHECK(p2.dispatch(disp)); + BOOST_CHECK(p3.dispatch(disp)); +} diff --git a/src/boost/libs/log/test/run/attr_named_scope.cpp b/src/boost/libs/log/test/run/attr_named_scope.cpp new file mode 100644 index 00000000..9429a36c --- /dev/null +++ b/src/boost/libs/log/test/run/attr_named_scope.cpp @@ -0,0 +1,190 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_named_scope.cpp + * \author Andrey Semashev + * \date 25.01.2009 + * + * \brief This header contains tests for the named scope attribute. + */ + +#define BOOST_TEST_MODULE attr_named_scope + +#include <sstream> +#include <boost/mpl/vector.hpp> +#include <boost/preprocessor/cat.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/attribute.hpp> +#include <boost/log/attributes/named_scope.hpp> +#include <boost/log/attributes/attribute_value.hpp> +#include <boost/log/attributes/value_extraction.hpp> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/utility/value_ref.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + + template< typename > + struct scope_test_data; + + template< > + struct scope_test_data< char > + { + static logging::string_literal scope1() { return logging::str_literal("scope1"); } + static logging::string_literal scope2() { return logging::str_literal("scope2"); } + static logging::string_literal file() { return logging::str_literal(__FILE__); } + }; + +} // namespace + +// The test verifies that the scope macros are defined +BOOST_AUTO_TEST_CASE(macros) +{ +#ifdef BOOST_LOG_USE_CHAR + BOOST_CHECK(BOOST_IS_DEFINED(BOOST_LOG_NAMED_SCOPE(name))); + BOOST_CHECK(BOOST_IS_DEFINED(BOOST_LOG_FUNCTION())); +#endif // BOOST_LOG_USE_CHAR +} + +// The test checks that scope tracking works correctly +BOOST_AUTO_TEST_CASE(scope_tracking) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + typedef attrs::named_scope_list scopes; + typedef attrs::named_scope_entry scope; + typedef scope_test_data< char > scope_data; + + named_scope attr; + + // First scope + const unsigned int line1 = __LINE__; + sentry scope1(scope_data::scope1(), scope_data::file(), line1); + + BOOST_CHECK(!named_scope::get_scopes().empty()); + BOOST_CHECK_EQUAL(named_scope::get_scopes().size(), 1UL); + + logging::attribute_value val = attr.get_value(); + BOOST_REQUIRE(!!val); + + logging::value_ref< scopes > sc = val.extract< scopes >(); + BOOST_REQUIRE(!!sc); + BOOST_REQUIRE(!sc->empty()); + BOOST_CHECK_EQUAL(sc->size(), 1UL); + + scope const& s1 = sc->front(); + BOOST_CHECK(s1.scope_name == scope_data::scope1()); + BOOST_CHECK(s1.file_name == scope_data::file()); + BOOST_CHECK(s1.line == line1); + + // Second scope + const unsigned int line2 = __LINE__; + scope new_scope(scope_data::scope2(), scope_data::file(), line2); + named_scope::push_scope(new_scope); + + BOOST_CHECK(!named_scope::get_scopes().empty()); + BOOST_CHECK_EQUAL(named_scope::get_scopes().size(), 2UL); + + val = attr.get_value(); + BOOST_REQUIRE(!!val); + + sc = val.extract< scopes >(); + BOOST_REQUIRE(!!sc); + BOOST_REQUIRE(!sc->empty()); + BOOST_CHECK_EQUAL(sc->size(), 2UL); + + scopes::const_iterator it = sc->begin(); + scope const& s2 = *(it++); + BOOST_CHECK(s2.scope_name == scope_data::scope1()); + BOOST_CHECK(s2.file_name == scope_data::file()); + BOOST_CHECK(s2.line == line1); + + scope const& s3 = *(it++); + BOOST_CHECK(s3.scope_name == scope_data::scope2()); + BOOST_CHECK(s3.file_name == scope_data::file()); + BOOST_CHECK(s3.line == line2); + + BOOST_CHECK(it == sc->end()); + + // Second scope goes out + named_scope::pop_scope(); + + BOOST_CHECK(!named_scope::get_scopes().empty()); + BOOST_CHECK_EQUAL(named_scope::get_scopes().size(), 1UL); + + val = attr.get_value(); + BOOST_REQUIRE(!!val); + + sc = val.extract< scopes >(); + BOOST_REQUIRE(!!sc); + BOOST_REQUIRE(!sc->empty()); + BOOST_CHECK_EQUAL(sc->size(), 1UL); + + scope const& s4 = sc->back(); // should be the same as front + BOOST_CHECK(s4.scope_name == scope_data::scope1()); + BOOST_CHECK(s4.file_name == scope_data::file()); + BOOST_CHECK(s4.line == line1); +} + +// The test checks that detaching from thread works correctly +BOOST_AUTO_TEST_CASE(detaching_from_thread) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + typedef attrs::named_scope_list scopes; + typedef scope_test_data< char > scope_data; + + named_scope attr; + + sentry scope1(scope_data::scope1(), scope_data::file(), __LINE__); + logging::attribute_value val1 = attr.get_value(); + val1.detach_from_thread(); + + sentry scope2(scope_data::scope2(), scope_data::file(), __LINE__); + logging::attribute_value val2 = attr.get_value(); + val2.detach_from_thread(); + + logging::value_ref< scopes > sc1 = val1.extract< scopes >(), sc2 = val2.extract< scopes >(); + BOOST_REQUIRE(!!sc1); + BOOST_REQUIRE(!!sc2); + BOOST_CHECK_EQUAL(sc1->size(), 1UL); + BOOST_CHECK_EQUAL(sc2->size(), 2UL); +} + +// The test checks that output streaming is possible +BOOST_AUTO_TEST_CASE(ostreaming) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + typedef scope_test_data< char > scope_data; + + sentry scope1(scope_data::scope1(), scope_data::file(), __LINE__); + sentry scope2(scope_data::scope2(), scope_data::file(), __LINE__); + + std::basic_ostringstream< char > strm; + strm << named_scope::get_scopes(); + + BOOST_CHECK(!strm.str().empty()); +} + +// The test checks that the scope list becomes thread-independent after copying +BOOST_AUTO_TEST_CASE(copying) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + typedef attrs::named_scope_list scopes; + typedef scope_test_data< char > scope_data; + + sentry scope1(scope_data::scope1(), scope_data::file(), __LINE__); + scopes sc = named_scope::get_scopes(); + sentry scope2(scope_data::scope2(), scope_data::file(), __LINE__); + BOOST_CHECK_EQUAL(sc.size(), 1UL); + BOOST_CHECK_EQUAL(named_scope::get_scopes().size(), 2UL); +} diff --git a/src/boost/libs/log/test/run/attr_sets_insertion_lookup.cpp b/src/boost/libs/log/test/run/attr_sets_insertion_lookup.cpp new file mode 100644 index 00000000..46fc6a1c --- /dev/null +++ b/src/boost/libs/log/test/run/attr_sets_insertion_lookup.cpp @@ -0,0 +1,101 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_sets_insertion_lookup.cpp + * \author Andrey Semashev + * \date 21.06.2014 + * + * \brief This header contains tests for the attribute and attribute value sets. This test performs special checks + * for insert() and find() methods that depend on the attribute name ids and the order in which + * insert() operations are invoked, see https://sourceforge.net/p/boost-log/discussion/710022/thread/e883db9a/. + */ + +#define BOOST_TEST_MODULE attr_sets_insertion_lookup + +#include <string> +#include <sstream> +#include <boost/config.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + +template< typename SetT, typename ValueT > +void test_insertion_lookup(SetT& values, ValueT const& value) +{ + // Initialize attribute names. Each name will gain a consecutive id. + logging::attribute_name names[20]; + for (unsigned int i = 0; i < sizeof(names) / sizeof(*names); ++i) + { + std::ostringstream strm; + strm << "Attr" << i; + names[i] = logging::attribute_name(strm.str()); + } + + // Insert attribute values in this exact order so that different cases in the hash table implementation are tested. + values.insert(names[17], value); + values.insert(names[1], value); + values.insert(names[8], value); + values.insert(names[9], value); + values.insert(names[10], value); + values.insert(names[16], value); + values.insert(names[0], value); + values.insert(names[11], value); + values.insert(names[12], value); + values.insert(names[13], value); + values.insert(names[14], value); + values.insert(names[15], value); + values.insert(names[18], value); + values.insert(names[19], value); + values.insert(names[4], value); + values.insert(names[5], value); + values.insert(names[7], value); + values.insert(names[6], value); + values.insert(names[2], value); + values.insert(names[3], value); + + // Check that all values are accessible through iteration and find() + for (unsigned int i = 0; i < sizeof(names) / sizeof(*names); ++i) + { + BOOST_CHECK_MESSAGE(values.find(names[i]) != values.end(), "Attribute " << names[i] << " (id: " << names[i].id() << ") not found by find()"); + + bool found_by_iteration = false; + for (typename SetT::const_iterator it = values.begin(), end = values.end(); it != end; ++it) + { + if (it->first == names[i]) + { + found_by_iteration = true; + break; + } + } + + BOOST_CHECK_MESSAGE(found_by_iteration, "Attribute " << names[i] << " (id: " << names[i].id() << ") not found by iteration"); + } +} + +} // namespace + +BOOST_AUTO_TEST_CASE(attributes) +{ + logging::attribute_set values; + attrs::constant< int > attr(10); + + test_insertion_lookup(values, attr); +} + +BOOST_AUTO_TEST_CASE(attribute_values) +{ + logging::attribute_value_set values; + attrs::constant< int > attr(10); + + test_insertion_lookup(values, attr.get_value()); +} diff --git a/src/boost/libs/log/test/run/attr_value_visitation.cpp b/src/boost/libs/log/test/run/attr_value_visitation.cpp new file mode 100644 index 00000000..f057fb17 --- /dev/null +++ b/src/boost/libs/log/test/run/attr_value_visitation.cpp @@ -0,0 +1,238 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 attr_value_visitation.cpp + * \author Andrey Semashev + * \date 21.01.2009 + * + * \brief This header contains tests for the attribute value extraction helpers. + */ + +#define BOOST_TEST_MODULE attr_value_visitation + +#include <string> +#include <boost/mpl/vector.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/tools/floating_point_comparison.hpp> +#include <boost/log/attributes/value_visitation.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include "char_definitions.hpp" + +namespace mpl = boost::mpl; +namespace logging = boost::log; +namespace attrs = logging::attributes; + +namespace { + + // The receiver functional object that verifies the extracted attribute values + struct my_receiver + { + typedef void result_type; + + enum type_expected + { + none_expected, + int_expected, + double_expected, + string_expected + }; + + my_receiver() : m_Expected(none_expected), m_Int(0), m_Double(0.0) {} + + void set_expected() + { + m_Expected = none_expected; + } + void set_expected(int value) + { + m_Expected = int_expected; + m_Int = value; + } + void set_expected(double value) + { + m_Expected = double_expected; + m_Double = value; + } + void set_expected(std::string const& value) + { + m_Expected = string_expected; + m_String = value; + } + + // Implement visitation logic for all supported types + void operator() (int const& value) + { + BOOST_CHECK_EQUAL(m_Expected, int_expected); + BOOST_CHECK_EQUAL(m_Int, value); + } + void operator() (double const& value) + { + BOOST_CHECK_EQUAL(m_Expected, double_expected); + BOOST_CHECK_CLOSE(m_Double, value, 0.001); + } + void operator() (std::string const& value) + { + BOOST_CHECK_EQUAL(m_Expected, string_expected); + BOOST_CHECK_EQUAL(m_String, value); + } + void operator() (char value) + { + // This one should not be called + BOOST_ERROR("The unexpected operator() has been called"); + } + + private: + type_expected m_Expected; + int m_Int; + double m_Double; + std::string m_String; + }; + +} // namespace + +// The test checks invokers specialized on a single attribute value type +BOOST_AUTO_TEST_CASE(single_type) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + my_receiver recv; + + logging::value_visitor_invoker< int > invoker1; + logging::value_visitor_invoker< double > invoker2; + logging::value_visitor_invoker< std::string > invoker3; + logging::value_visitor_invoker< char > invoker4; + + // These two extractors will find their values in the set + recv.set_expected(10); + BOOST_CHECK(invoker1(data::attr1(), values1, recv)); + + recv.set_expected(5.5); + BOOST_CHECK(invoker2(data::attr2(), values1, recv)); + + // This one will not + recv.set_expected(); + BOOST_CHECK(!invoker3(data::attr3(), values1, recv)); + + // But it will find it in this set + set1[data::attr3()] = attr3; + + attr_values values2(set1, set2, set3); + values2.freeze(); + + recv.set_expected("Hello, world!"); + BOOST_CHECK(invoker3(data::attr3(), values2, recv)); + + // This one will find the sought attribute value, but it will have an incorrect type + recv.set_expected(); + BOOST_CHECK(!invoker4(data::attr1(), values1, recv)); + + // This one is the same, but there is a value of the requested type in the set + BOOST_CHECK(!invoker1(data::attr2(), values1, recv)); +} + + +// The test checks invokers specialized with type lists +BOOST_AUTO_TEST_CASE(multiple_types) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef test_data< char > data; + typedef mpl::vector< int, double, std::string, char >::type types; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + my_receiver recv; + + logging::value_visitor_invoker< types > invoker; + + // These two extractors will find their values in the set + recv.set_expected(10); + BOOST_CHECK(invoker(data::attr1(), values1, recv)); + + recv.set_expected(5.5); + BOOST_CHECK(invoker(data::attr2(), values1, recv)); + + // This one will not + recv.set_expected(); + BOOST_CHECK(!invoker(data::attr3(), values1, recv)); + + // But it will find it in this set + set1[data::attr3()] = attr3; + + attr_values values2(set1, set2, set3); + values2.freeze(); + + recv.set_expected("Hello, world!"); + BOOST_CHECK(invoker(data::attr3(), values2, recv)); +} + +// The test verifies the visit function +BOOST_AUTO_TEST_CASE(visit_function) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef test_data< char > data; + typedef mpl::vector< int, double, std::string, char >::type types; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + my_receiver recv; + + // These two extractors will find their values in the set + recv.set_expected(10); + BOOST_CHECK(logging::visit< types >(data::attr1(), values1, recv)); + + recv.set_expected(5.5); + BOOST_CHECK(logging::visit< double >(data::attr2(), values1, recv)); + + // These will not + recv.set_expected(); + BOOST_CHECK(!logging::visit< types >(data::attr3(), values1, recv)); + BOOST_CHECK(!logging::visit< char >(data::attr1(), values1, recv)); + + // But it will find it in this set + set1[data::attr3()] = attr3; + + attr_values values2(set1, set2, set3); + values2.freeze(); + + recv.set_expected("Hello, world!"); + BOOST_CHECK(logging::visit< std::string >(data::attr3(), values2, recv)); +} diff --git a/src/boost/libs/log/test/run/core.cpp b/src/boost/libs/log/test/run/core.cpp new file mode 100644 index 00000000..69b42647 --- /dev/null +++ b/src/boost/libs/log/test/run/core.cpp @@ -0,0 +1,243 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 core.cpp + * \author Andrey Semashev + * \date 08.02.2009 + * + * \brief This header contains tests for the logging core. + */ + +#define BOOST_TEST_MODULE core + +#include <cstddef> +#include <map> +#include <string> +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/move/utility_core.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/core/core.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/sinks/sink.hpp> +#include <boost/log/core/record.hpp> +#ifndef BOOST_LOG_NO_THREADS +#include <boost/thread/thread.hpp> +#endif // BOOST_LOG_NO_THREADS +#include "char_definitions.hpp" +#include "test_sink.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace sinks = logging::sinks; +namespace expr = logging::expressions; + +// The test checks that message filtering works +BOOST_AUTO_TEST_CASE(filtering) +{ + typedef logging::attribute_set attr_set; + typedef logging::core core; + typedef logging::record record_type; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + boost::shared_ptr< core > pCore = core::get(); + boost::shared_ptr< test_sink > pSink(new test_sink()); + pCore->add_sink(pSink); + + // No filtering at all + { + record_type rec = pCore->open_record(set1); + BOOST_REQUIRE(rec); + pCore->push_record(boost::move(rec)); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + } + + // Core-level filtering + { + pCore->set_filter(expr::has_attr(data::attr3())); + record_type rec = pCore->open_record(set1); + BOOST_CHECK(!rec); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + } + { + pCore->set_filter(expr::has_attr(data::attr2())); + record_type rec = pCore->open_record(set1); + BOOST_REQUIRE(rec); + pCore->push_record(boost::move(rec)); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + } + + // Sink-level filtering + { + pCore->reset_filter(); + pSink->set_filter(expr::has_attr(data::attr2())); + record_type rec = pCore->open_record(set1); + BOOST_REQUIRE(rec); + pCore->push_record(boost::move(rec)); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + } + { + pSink->set_filter(expr::has_attr(data::attr3())); + record_type rec = pCore->open_record(set1); + BOOST_CHECK(!rec); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + pSink->reset_filter(); + } + // Only one sink of the two accepts the record + { + pSink->set_filter(expr::has_attr(data::attr2())); + + boost::shared_ptr< test_sink > pSink2(new test_sink()); + pCore->add_sink(pSink2); + pSink2->set_filter(expr::has_attr(data::attr3())); + + record_type rec = pCore->open_record(set1); + BOOST_REQUIRE(rec); + pCore->push_record(boost::move(rec)); + + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + + BOOST_CHECK_EQUAL(pSink2->m_RecordCounter, 0UL); + BOOST_CHECK_EQUAL(pSink2->m_Consumed[data::attr1()], 0UL); + BOOST_CHECK_EQUAL(pSink2->m_Consumed[data::attr2()], 0UL); + BOOST_CHECK_EQUAL(pSink2->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink2->m_Consumed[data::attr4()], 0UL); + pCore->remove_sink(pSink2); + } + + pCore->remove_sink(pSink); + pCore->reset_filter(); +} + +#ifndef BOOST_LOG_NO_THREADS +namespace { + + //! A test routine that runs in a separate thread + void thread_attributes_test() + { + typedef test_data< char > data; + typedef logging::core core; + typedef logging::record record_type; + typedef logging::attribute_set attr_set; + attrs::constant< short > attr4(255); + + boost::shared_ptr< core > pCore = core::get(); + pCore->add_thread_attribute(data::attr4(), attr4); + + attr_set set1; + record_type rec = pCore->open_record(set1); + BOOST_CHECK(rec); + if (rec) + pCore->push_record(boost::move(rec)); + } + +} // namespace +#endif // BOOST_LOG_NO_THREADS + +// The test checks that global and thread-specific attributes work +BOOST_AUTO_TEST_CASE(attributes) +{ + typedef logging::attribute_set attr_set; + typedef logging::core core; + typedef logging::record record_type; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1; + set1[data::attr1()] = attr1; + + boost::shared_ptr< core > pCore = core::get(); + boost::shared_ptr< test_sink > pSink(new test_sink()); + pCore->add_sink(pSink); + + attr_set::iterator itGlobal = pCore->add_global_attribute(data::attr2(), attr2).first; + attr_set::iterator itThread = pCore->add_thread_attribute(data::attr3(), attr3).first; + + { + attr_set glob = pCore->get_global_attributes(); + BOOST_CHECK_EQUAL(glob.size(), 1UL); + BOOST_CHECK_EQUAL(glob.count(data::attr2()), 1UL); + + attr_set thr = pCore->get_thread_attributes(); + BOOST_CHECK_EQUAL(thr.size(), 1UL); + BOOST_CHECK_EQUAL(thr.count(data::attr3()), 1UL); + } + { + record_type rec = pCore->open_record(set1); + BOOST_REQUIRE(rec); + pCore->push_record(boost::move(rec)); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 0UL); + pSink->clear(); + } +#ifndef BOOST_LOG_NO_THREADS + { + boost::thread th(&thread_attributes_test); + th.join(); + BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr2()], 1UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr3()], 0UL); + BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr4()], 1UL); + pSink->clear(); + + // Thread-specific attributes must not interfere + attr_set thr = pCore->get_thread_attributes(); + BOOST_CHECK_EQUAL(thr.size(), 1UL); + BOOST_CHECK_EQUAL(thr.count(data::attr3()), 1UL); + } +#endif // BOOST_LOG_NO_THREADS + + pCore->remove_global_attribute(itGlobal); + pCore->remove_thread_attribute(itThread); + pCore->remove_sink(pSink); +} diff --git a/src/boost/libs/log/test/run/filt_attr.cpp b/src/boost/libs/log/test/run/filt_attr.cpp new file mode 100644 index 00000000..23790812 --- /dev/null +++ b/src/boost/libs/log/test/run/filt_attr.cpp @@ -0,0 +1,403 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_attr.cpp + * \author Andrey Semashev + * \date 31.01.2009 + * + * \brief This header contains tests for the \c attr filter. + */ + +#define BOOST_TEST_MODULE filt_attr + +#include <memory> +#include <string> +#include <algorithm> +#include <boost/regex.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/phoenix/bind.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/support/regex.hpp> +#include <boost/log/expressions.hpp> +#include "char_definitions.hpp" + +namespace phoenix = boost::phoenix; + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that general conditions work +BOOST_AUTO_TEST_CASE(general_conditions) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::attr< int >(data::attr1()) == 10; + BOOST_CHECK(f(values1)); + + f = expr::attr< int >(data::attr1()) < 0; + BOOST_CHECK(!f(values1)); + + f = expr::attr< float >(data::attr1()).or_throw() > 0; + BOOST_CHECK_THROW(f(values1), logging::runtime_error); + f = expr::attr< float >(data::attr1()) > 0; + BOOST_CHECK(!f(values1)); + + f = expr::attr< int >(data::attr4()).or_throw() >= 1; + BOOST_CHECK_THROW(f(values1), logging::runtime_error); + f = expr::attr< int >(data::attr4()) >= 1; + BOOST_CHECK(!f(values1)); + + f = expr::attr< int >(data::attr4()) < 1; + BOOST_CHECK(!f(values1)); + + f = expr::attr< logging::numeric_types >(data::attr2()) > 5; + BOOST_CHECK(f(values1)); + + f = expr::attr< std::string >(data::attr3()) == "Hello, world!"; + BOOST_CHECK(f(values1)); + + f = expr::attr< std::string >(data::attr3()) > "AAA"; + BOOST_CHECK(f(values1)); +} + +// The test checks that is_in_range condition works +BOOST_AUTO_TEST_CASE(in_range_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::is_in_range(expr::attr< int >(data::attr1()), 5, 20); + BOOST_CHECK(f(values1)); + + f = expr::is_in_range(expr::attr< int >(data::attr1()), 5, 10); + BOOST_CHECK(!f(values1)); + + f = expr::is_in_range(expr::attr< int >(data::attr1()), 10, 20); + BOOST_CHECK(f(values1)); + + f = expr::is_in_range(expr::attr< logging::numeric_types >(data::attr2()), 5, 6); + BOOST_CHECK(f(values1)); + + f = expr::is_in_range(expr::attr< std::string >(data::attr3()), "AAA", "zzz"); + BOOST_CHECK(f(values1)); + + // Check that strings are saved into the filter by value + char buf1[128]; + char buf2[128]; + std::strcpy(buf1, "AAA"); + std::strcpy(buf2, "zzz"); + f = expr::is_in_range(expr::attr< std::string >(data::attr3()), buf1, buf2); + std::fill_n(buf1, sizeof(buf1), static_cast< char >(0)); + std::fill_n(buf2, sizeof(buf2), static_cast< char >(0)); + BOOST_CHECK(f(values1)); + + std::strcpy(buf1, "AAA"); + std::strcpy(buf2, "zzz"); + f = expr::is_in_range(expr::attr< std::string >(data::attr3()), + static_cast< const char* >(buf1), static_cast< const char* >(buf2)); + std::fill_n(buf1, sizeof(buf1), static_cast< char >(0)); + std::fill_n(buf2, sizeof(buf2), static_cast< char >(0)); + BOOST_CHECK(f(values1)); +} + +namespace { + + struct predicate + { + typedef bool result_type; + + explicit predicate(unsigned int& present_counter, bool& result) : + m_PresentCounter(present_counter), + m_Result(result) + { + } + + template< typename T, typename TagT > + result_type operator() (logging::value_ref< T, TagT > const& val) const + { + m_PresentCounter += !val.empty(); + return m_Result; + } + + private: + unsigned int& m_PresentCounter; + bool& m_Result; + }; + +} // namespace + +// The test checks that phoenix::bind interaction works +BOOST_AUTO_TEST_CASE(bind_support_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + unsigned int present_counter = 0; + bool predicate_result = false; + + filter f = phoenix::bind(predicate(present_counter, predicate_result), expr::attr< int >(data::attr1())); + BOOST_CHECK_EQUAL(f(values1), predicate_result); + BOOST_CHECK_EQUAL(present_counter, 1U); + + predicate_result = true; + BOOST_CHECK_EQUAL(f(values1), predicate_result); + BOOST_CHECK_EQUAL(present_counter, 2U); + + f = phoenix::bind(predicate(present_counter, predicate_result), expr::attr< logging::numeric_types >(data::attr2())); + BOOST_CHECK_EQUAL(f(values1), predicate_result); + BOOST_CHECK_EQUAL(present_counter, 3U); + + f = phoenix::bind(predicate(present_counter, predicate_result), expr::attr< int >(data::attr2()).or_throw()); + BOOST_CHECK_THROW(f(values1), logging::runtime_error); + f = phoenix::bind(predicate(present_counter, predicate_result), expr::attr< int >(data::attr2())); + BOOST_CHECK_EQUAL(f(values1), true); + BOOST_CHECK_EQUAL(present_counter, 3U); + + f = phoenix::bind(predicate(present_counter, predicate_result), expr::attr< int >(data::attr4()).or_throw()); + BOOST_CHECK_THROW(f(values1), logging::runtime_error); + f = phoenix::bind(predicate(present_counter, predicate_result), expr::attr< int >(data::attr4())); + BOOST_CHECK_EQUAL(f(values1), true); + BOOST_CHECK_EQUAL(present_counter, 3U); +} + +// The test checks that begins_with condition works +BOOST_AUTO_TEST_CASE(begins_with_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::begins_with(expr::attr< std::string >(data::attr3()), "Hello"); + BOOST_CHECK(f(values1)); + + f = expr::begins_with(expr::attr< std::string >(data::attr3()), "hello"); + BOOST_CHECK(!f(values1)); + + f = expr::begins_with(expr::attr< std::string >(data::attr3()).or_throw(), "Bye"); + BOOST_CHECK(!f(values1)); + + f = expr::begins_with(expr::attr< std::string >(data::attr3()).or_throw(), "world!"); + BOOST_CHECK(!f(values1)); + + f = expr::begins_with(expr::attr< std::string >(data::attr2()), "Hello"); + BOOST_CHECK(!f(values1)); + + f = expr::begins_with(expr::attr< std::string >(data::attr4()), "Hello"); + BOOST_CHECK(!f(values1)); +} + +// The test checks that ends_with condition works +BOOST_AUTO_TEST_CASE(ends_with_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::ends_with(expr::attr< std::string >(data::attr3()), "world!"); + BOOST_CHECK(f(values1)); + + f = expr::ends_with(expr::attr< std::string >(data::attr3()), "World!"); + BOOST_CHECK(!f(values1)); + + f = expr::ends_with(expr::attr< std::string >(data::attr3()).or_throw(), "Bye"); + BOOST_CHECK(!f(values1)); + + f = expr::ends_with(expr::attr< std::string >(data::attr3()).or_throw(), "Hello"); + BOOST_CHECK(!f(values1)); + + f = expr::ends_with(expr::attr< std::string >(data::attr2()), "world!"); + BOOST_CHECK(!f(values1)); + + f = expr::ends_with(expr::attr< std::string >(data::attr4()), "world!"); + BOOST_CHECK(!f(values1)); +} + +// The test checks that contains condition works +BOOST_AUTO_TEST_CASE(contains_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::contains(expr::attr< std::string >(data::attr3()), "Hello"); + BOOST_CHECK(f(values1)); + + f = expr::contains(expr::attr< std::string >(data::attr3()), "hello"); + BOOST_CHECK(!f(values1)); + + f = expr::contains(expr::attr< std::string >(data::attr3()).or_throw(), "o, w"); + BOOST_CHECK(f(values1)); + + f = expr::contains(expr::attr< std::string >(data::attr3()).or_throw(), "world!"); + BOOST_CHECK(f(values1)); + + f = expr::contains(expr::attr< std::string >(data::attr2()), "Hello"); + BOOST_CHECK(!f(values1)); + + f = expr::contains(expr::attr< std::string >(data::attr4()), "Hello"); + BOOST_CHECK(!f(values1)); +} + +// The test checks that matches condition works +BOOST_AUTO_TEST_CASE(matches_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + boost::regex rex("hello"); + filter f = expr::matches(expr::attr< std::string >(data::attr3()), rex); + BOOST_CHECK(!f(values1)); + + rex = ".*world.*"; + f = expr::matches(expr::attr< std::string >(data::attr3()).or_throw(), rex); + BOOST_CHECK(f(values1)); + + rex = ".*"; + f = expr::matches(expr::attr< std::string >(data::attr2()), rex); + BOOST_CHECK(!f(values1)); + + f = expr::matches(expr::attr< std::string >(data::attr4()), rex); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = + expr::attr< int >(data::attr1()) <= 10 || + expr::is_in_range(expr::attr< double >(data::attr2()), 2.2, 7.7); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::attr< int >(data::attr1()) == 10 && + expr::begins_with(expr::attr< std::string >(data::attr3()), "Hello"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} diff --git a/src/boost/libs/log/test/run/filt_has_attr.cpp b/src/boost/libs/log/test/run/filt_has_attr.cpp new file mode 100644 index 00000000..01d2d10a --- /dev/null +++ b/src/boost/libs/log/test/run/filt_has_attr.cpp @@ -0,0 +1,102 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_has_attr.cpp + * \author Andrey Semashev + * \date 31.01.2009 + * + * \brief This header contains tests for the \c has_attr filter. + */ + +#define BOOST_TEST_MODULE filt_has_attr + +#include <string> +#include <boost/mpl/vector.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that the filter detects the attribute value presence +BOOST_AUTO_TEST_CASE(presence_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::has_attr(data::attr1()); + BOOST_CHECK(f(values1)); + + f = expr::has_attr(data::attr4()); + BOOST_CHECK(!f(values1)); + + f = expr::has_attr< double >(data::attr2()); + BOOST_CHECK(f(values1)); + + f = expr::has_attr< double >(data::attr3()); + BOOST_CHECK(!f(values1)); + + typedef mpl::vector< unsigned int, char, std::string >::type value_types; + f = expr::has_attr< value_types >(data::attr3()); + BOOST_CHECK(f(values1)); + + f = expr::has_attr< value_types >(data::attr1()); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = expr::has_attr(data::attr1()) || expr::has_attr< double >(data::attr2()); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::has_attr(data::attr1()) && expr::has_attr< double >(data::attr2()); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} diff --git a/src/boost/libs/log/test/run/filt_matches_boost_regex.cpp b/src/boost/libs/log/test/run/filt_matches_boost_regex.cpp new file mode 100644 index 00000000..c34fc731 --- /dev/null +++ b/src/boost/libs/log/test/run/filt_matches_boost_regex.cpp @@ -0,0 +1,107 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_matches_boost_regex.cpp + * \author Andrey Semashev + * \date 30.03.2014 + * + * \brief This header contains tests for the \c matches filter with Boost.Regex backend. + */ + +#define BOOST_TEST_MODULE filt_matches_boost_regex + +#include <string> +#include <boost/regex.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/support/regex.hpp> +#include <boost/log/utility/string_literal.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that string matching works +BOOST_AUTO_TEST_CASE(matching_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + typedef boost::regex regex_type; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), regex_type("\\d+\\.\\d+\\.\\d+\\.\\d+")); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr1(), regex_type("[a-z]*")); + BOOST_CHECK(!f(values1)); + + f = expr::matches< logging::string_literal >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr3(), regex_type("Hello, world!")); + BOOST_CHECK(f(values1)); + + // Attribute value not present + f = expr::matches< std::string >(data::attr4(), regex_type(".*")); + BOOST_CHECK(!f(values1)); + + // Attribute value type does not match + f = expr::matches< std::string >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + typedef boost::regex regex_type; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), regex_type("\\d+\\.\\d+\\.\\d+\\.\\d+")) || expr::matches< logging::string_literal >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::matches< std::string >(data::attr1(), regex_type("\\d+\\.\\d+\\.\\d+\\.\\d+")) && expr::matches< logging::string_literal >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} diff --git a/src/boost/libs/log/test/run/filt_matches_spirit_classic.cpp b/src/boost/libs/log/test/run/filt_matches_spirit_classic.cpp new file mode 100644 index 00000000..5e3ca044 --- /dev/null +++ b/src/boost/libs/log/test/run/filt_matches_spirit_classic.cpp @@ -0,0 +1,115 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_matches_spirit_classic.cpp + * \author Andrey Semashev + * \date 30.03.2014 + * + * \brief This header contains tests for the \c matches filter with Boost.Spirit.Classic backend. + */ + +#define BOOST_TEST_MODULE filt_matches_spirit_classic + +#include <boost/log/detail/config.hpp> + +#if !defined(BOOST_LOG_NO_THREADS) +#define BOOST_SPIRIT_THREADSAFE 1 +#endif + +#include <string> +#include <boost/spirit/include/classic_core.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/support/spirit_classic.hpp> +#include <boost/log/utility/string_literal.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace spirit = boost::spirit::classic; + +// The test checks that string matching works +BOOST_AUTO_TEST_CASE(matching_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), spirit::uint_p >> '.' >> spirit::uint_p >> '.' >> spirit::uint_p >> '.' >> spirit::uint_p); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr1(), *spirit::lower_p); + BOOST_CHECK(!f(values1)); + + f = expr::matches< logging::string_literal >(data::attr2(), *spirit::upper_p >> spirit::space_p >> *spirit::lower_p >> spirit::space_p >> *spirit::alpha_p); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr3(), spirit::str_p("Hello, world!")); + BOOST_CHECK(f(values1)); + + // Attribute value not present + f = expr::matches< std::string >(data::attr4(), *spirit::anychar_p); + BOOST_CHECK(!f(values1)); + + // Attribute value type does not match + f = expr::matches< std::string >(data::attr2(), *spirit::upper_p >> spirit::space_p >> *spirit::lower_p >> spirit::space_p >> *spirit::alpha_p); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), +spirit::digit_p >> '.' >> +spirit::digit_p >> '.' >> +spirit::digit_p >> '.' >> +spirit::digit_p) + || expr::matches< logging::string_literal >(data::attr2(), *spirit::upper_p >> spirit::space_p >> *spirit::lower_p >> spirit::space_p >> *spirit::alpha_p); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::matches< std::string >(data::attr1(), +spirit::digit_p >> '.' >> +spirit::digit_p >> '.' >> +spirit::digit_p >> '.' >> +spirit::digit_p) + && expr::matches< logging::string_literal >(data::attr2(), *spirit::upper_p >> spirit::space_p >> *spirit::lower_p >> spirit::space_p >>*spirit::alpha_p); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} diff --git a/src/boost/libs/log/test/run/filt_matches_spirit_qi.cpp b/src/boost/libs/log/test/run/filt_matches_spirit_qi.cpp new file mode 100644 index 00000000..c85ca011 --- /dev/null +++ b/src/boost/libs/log/test/run/filt_matches_spirit_qi.cpp @@ -0,0 +1,114 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_matches_spirit_qi.cpp + * \author Andrey Semashev + * \date 30.03.2014 + * + * \brief This header contains tests for the \c matches filter with Boost.Spirit.Qi backend. + */ + +#define BOOST_TEST_MODULE filt_matches_spirit_qi + +#include <string> +#include <boost/spirit/include/qi_core.hpp> +#include <boost/spirit/include/qi_rule.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/support/spirit_qi.hpp> +#include <boost/log/utility/string_literal.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace spirit = boost::spirit::qi; + +// The test checks that string matching works +BOOST_AUTO_TEST_CASE(matching_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), spirit::uint_ >> '.' >> spirit::uint_ >> '.' >> spirit::uint_ >> '.' >> spirit::uint_); + BOOST_CHECK(f(values1)); + + // Make sure rules also work + f = expr::matches< std::string >(data::attr1(), spirit::rule< std::string::const_iterator, void() >(spirit::uint_ >> '.' >> spirit::uint_ >> '.' >> spirit::uint_ >> '.' >> spirit::uint_)); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr1(), *spirit::lower); + BOOST_CHECK(!f(values1)); + + f = expr::matches< logging::string_literal >(data::attr2(), *spirit::upper >> spirit::space >> *spirit::lower >> spirit::space >> *spirit::alpha); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr3(), spirit::lit("Hello, world!")); + BOOST_CHECK(f(values1)); + + // Attribute value not present + f = expr::matches< std::string >(data::attr4(), *spirit::char_); + BOOST_CHECK(!f(values1)); + + // Attribute value type does not match + f = expr::matches< std::string >(data::attr2(), *spirit::upper >> spirit::space >> *spirit::lower >> spirit::space >> *spirit::alpha); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), +spirit::digit >> '.' >> +spirit::digit >> '.' >> +spirit::digit >> '.' >> +spirit::digit) + || expr::matches< logging::string_literal >(data::attr2(), *spirit::upper >> spirit::space >> *spirit::lower >> spirit::space >> *spirit::alpha); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::matches< std::string >(data::attr1(), +spirit::digit >> '.' >> +spirit::digit >> '.' >> +spirit::digit >> '.' >> +spirit::digit) + && expr::matches< logging::string_literal >(data::attr2(), *spirit::upper >> spirit::space >> *spirit::lower >> spirit::space >> *spirit::alpha); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} diff --git a/src/boost/libs/log/test/run/filt_matches_std_regex.cpp b/src/boost/libs/log/test/run/filt_matches_std_regex.cpp new file mode 100644 index 00000000..22455175 --- /dev/null +++ b/src/boost/libs/log/test/run/filt_matches_std_regex.cpp @@ -0,0 +1,120 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_matches_std_regex.cpp + * \author Andrey Semashev + * \date 30.03.2014 + * + * \brief This header contains tests for the \c matches filter with \c std::regex backend. + */ + +#define BOOST_TEST_MODULE filt_matches_std_regex + +#include <boost/config.hpp> + +#if !defined(BOOST_NO_CXX11_HDR_REGEX) + +#include <string> +#include <regex> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/support/std_regex.hpp> +#include <boost/log/utility/string_literal.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that string matching works +BOOST_AUTO_TEST_CASE(matching_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + typedef std::regex regex_type; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), regex_type("\\d+\\.\\d+\\.\\d+\\.\\d+")); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr1(), regex_type("[a-z]*")); + BOOST_CHECK(!f(values1)); + + f = expr::matches< logging::string_literal >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr3(), regex_type("Hello, world!")); + BOOST_CHECK(f(values1)); + + // Attribute value not present + f = expr::matches< std::string >(data::attr4(), regex_type(".*")); + BOOST_CHECK(!f(values1)); + + // Attribute value type does not match + f = expr::matches< std::string >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + typedef std::regex regex_type; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), regex_type("\\d+\\.\\d+\\.\\d+\\.\\d+")) || expr::matches< logging::string_literal >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::matches< std::string >(data::attr1(), regex_type("\\d+\\.\\d+\\.\\d+\\.\\d+")) && expr::matches< logging::string_literal >(data::attr2(), regex_type("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} + +#else // !defined(BOOST_NO_CXX11_HDR_REGEX) + +int main(int, char*[]) +{ + return 0; +} + +#endif // !defined(BOOST_NO_CXX11_HDR_REGEX) diff --git a/src/boost/libs/log/test/run/filt_matches_xpressive.cpp b/src/boost/libs/log/test/run/filt_matches_xpressive.cpp new file mode 100644 index 00000000..44ee0a61 --- /dev/null +++ b/src/boost/libs/log/test/run/filt_matches_xpressive.cpp @@ -0,0 +1,107 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 filt_matches_xpressive.cpp + * \author Andrey Semashev + * \date 30.03.2014 + * + * \brief This header contains tests for the \c matches filter with Boost.Xpressive backend. + */ + +#define BOOST_TEST_MODULE filt_matches_xpressive + +#include <string> +#include <boost/xpressive/xpressive_dynamic.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/support/xpressive.hpp> +#include <boost/log/utility/string_literal.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that string matching works +BOOST_AUTO_TEST_CASE(matching_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + typedef boost::xpressive::basic_regex< const char* > regex_type; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), regex_type::compile("\\d+\\.\\d+\\.\\d+\\.\\d+")); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr1(), regex_type::compile("[a-z]*")); + BOOST_CHECK(!f(values1)); + + f = expr::matches< logging::string_literal >(data::attr2(), regex_type::compile("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(f(values1)); + + f = expr::matches< std::string >(data::attr3(), regex_type::compile("Hello, world!")); + BOOST_CHECK(f(values1)); + + // Attribute value not present + f = expr::matches< std::string >(data::attr4(), regex_type::compile(".*")); + BOOST_CHECK(!f(values1)); + + // Attribute value type does not match + f = expr::matches< std::string >(data::attr2(), regex_type::compile("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); +} + +// The test checks that the filter composition works +BOOST_AUTO_TEST_CASE(composition_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + typedef boost::xpressive::basic_regex< const char* > regex_type; + + attrs::constant< std::string > attr1("127.0.0.1"); + attrs::constant< logging::string_literal > attr2(logging::str_literal("BIG brown FoX")); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + filter f = expr::matches< std::string >(data::attr1(), regex_type::compile("\\d+\\.\\d+\\.\\d+\\.\\d+")) || expr::matches< logging::string_literal >(data::attr2(), regex_type::compile("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + + f = expr::matches< std::string >(data::attr1(), regex_type::compile("\\d+\\.\\d+\\.\\d+\\.\\d+")) && expr::matches< logging::string_literal >(data::attr2(), regex_type::compile("[A-Z]* [a-z]* [A-Za-z]*")); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); +} diff --git a/src/boost/libs/log/test/run/form_attr.cpp b/src/boost/libs/log/test/run/form_attr.cpp new file mode 100644 index 00000000..ef2deab7 --- /dev/null +++ b/src/boost/libs/log/test/run/form_attr.cpp @@ -0,0 +1,123 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_attr.cpp + * \author Andrey Semashev + * \date 01.02.2009 + * + * \brief This header contains tests for the \c attr formatter. + */ + +#define BOOST_TEST_MODULE form_attr + +#include <memory> +#include <string> +#include <iomanip> +#include <ostream> +#include <algorithm> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + + class my_class + { + int m_Data; + + public: + explicit my_class(int data) : m_Data(data) {} + + template< typename CharT, typename TraitsT > + friend std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_class const& obj) + { + strm << "[data: " << obj.m_Data << "]"; + return strm; + } + }; + +} // namespace + +// The test checks that default formatting work +BOOST_AUTO_TEST_CASE_TEMPLATE(default_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_data< CharT > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< my_class > attr3(my_class(77)); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + record_view rec = make_record_view(set1); + + // Check for various modes of attribute value type specification + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::attr< int >(data::attr1()) << expr::attr< double >(data::attr2()); + f(rec, strm1); + strm2 << 10 << 5.5; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::attr< logging::numeric_types >(data::attr1()) << expr::attr< logging::numeric_types >(data::attr2()); + f(rec, strm1); + strm2 << 10 << 5.5; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + // Check that custom types as attribute values are also supported + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::attr< my_class >(data::attr3()); + f(rec, strm1); + strm2 << my_class(77); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + // Check how missing attribute values are handled + { + string str1; + osstream strm1(str1); + formatter f = expr::stream + << expr::attr< int >(data::attr1()) + << expr::attr< int >(data::attr4()).or_throw() + << expr::attr< double >(data::attr2()); + BOOST_CHECK_THROW(f(rec, strm1), logging::runtime_error); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream + << expr::attr< int >(data::attr1()) + << expr::attr< int >(data::attr4()) + << expr::attr< double >(data::attr2()); + f(rec, strm1); + strm2 << 10 << 5.5; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_auto_newline.cpp b/src/boost/libs/log/test/run/form_auto_newline.cpp new file mode 100644 index 00000000..0ce38e67 --- /dev/null +++ b/src/boost/libs/log/test/run/form_auto_newline.cpp @@ -0,0 +1,94 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file form_auto_newline.cpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * \brief This header contains tests for the auto_newline formatter. + */ + +#define BOOST_TEST_MODULE form_auto_newline + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace expr = logging::expressions; + +// Test appending a newline to a non-empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_non_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + typedef logging::record_view record_view; + typedef logging::basic_formatter< char_type > formatter; + + record_view rec = make_record_view(); + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + formatter f = expr::stream << "Hello" << expr::auto_newline; + f(rec, strm_fmt); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test appending a newline to an empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + typedef logging::record_view record_view; + typedef logging::basic_formatter< char_type > formatter; + + record_view rec = make_record_view(); + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + formatter f = expr::stream << expr::auto_newline; + f(rec, strm_fmt); + + ostream_type strm_correct; + strm_correct << "\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test not appending a newline to a non-empty string which already ends with a newline +BOOST_AUTO_TEST_CASE_TEMPLATE(not_append_if_ends_with_a_newline, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + typedef logging::record_view record_view; + typedef logging::basic_formatter< char_type > formatter; + + record_view rec = make_record_view(); + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + formatter f = expr::stream << "Hello\n" << expr::auto_newline; + f(rec, strm_fmt); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} diff --git a/src/boost/libs/log/test/run/form_c_decor.cpp b/src/boost/libs/log/test/run/form_c_decor.cpp new file mode 100644 index 00000000..442a9392 --- /dev/null +++ b/src/boost/libs/log/test/run/form_c_decor.cpp @@ -0,0 +1,126 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_c_decor.cpp + * \author Andrey Semashev + * \date 26.09.2015 + * + * \brief This header contains tests for the \c c_decor and \c c_ascii_decor formatter. + */ + +#define BOOST_TEST_MODULE form_c_decor + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions/attr.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/expressions/formatters/c_decorator.hpp> +#include <boost/log/expressions/formatters/stream.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + +template< typename > +struct test_strings; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct test_strings< char > : public test_data< char > +{ + static const char* chars() { return "\a\b\f\n\r\t\v !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const char* escaped_chars() { return "\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&\\'()*+,-./0123456789:;<=>\\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + + static const char* special_chars() { return "\a\b\f\n\r\t\v !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82"; } + static const char* escaped_special_chars() { return "\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82"; } +}; +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct test_strings< wchar_t > : public test_data< wchar_t > +{ + static const wchar_t* chars() { return L"\a\b\f\n\r\t\v !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const wchar_t* escaped_chars() { return L"\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&\\'()*+,-./0123456789:;<=>\\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + + static const wchar_t* special_chars() { return L"\a\b\f\n\r\t\v !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82"; } + static const wchar_t* escaped_special_chars() + { + if (sizeof(wchar_t) == 1) + return L"\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82"; + if (sizeof(wchar_t) == 2) + return L"\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x007f\\x0080\\x0081\\x0082"; + if (sizeof(wchar_t) == 4) + return L"\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x0000007f\\x00000080\\x00000081\\x00000082"; + + return L"\\a\\b\\f\\n\\r\\t\\v !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x000000000000007f\\x0000000000000080\\x0000000000000081\\x0000000000000082"; + } +}; +#endif + +} // namespace + +BOOST_AUTO_TEST_CASE_TEMPLATE(c_decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::make_c_decor< CharT >()[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << data::escaped_chars(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(c_ascii_decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::make_c_ascii_decor< CharT >()[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << data::escaped_chars(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_char_decor.cpp b/src/boost/libs/log/test/run/form_char_decor.cpp new file mode 100644 index 00000000..7ef45b96 --- /dev/null +++ b/src/boost/libs/log/test/run/form_char_decor.cpp @@ -0,0 +1,174 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_char_decor.cpp + * \author Andrey Semashev + * \date 26.09.2015 + * + * \brief This header contains tests for the \c char_decor formatter. + */ + +#define BOOST_TEST_MODULE form_char_decor + +#include <string> +#include <vector> +#include <utility> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions/attr.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/expressions/formatters/char_decorator.hpp> +#include <boost/log/expressions/formatters/stream.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + +template< typename > +struct test_strings; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct test_strings< char > : public test_data< char > +{ + static const char* printable_chars() { return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const char* escaped_chars() { return " !\"#$%&'()*+,-./0123456789:;<=>?@aBBBDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`123defghijklmnopqrstuvwxyz{|}~"; } + + static std::vector< std::string > patterns() + { + std::vector< std::string > v; + v.push_back("A"); + v.push_back("B"); + v.push_back("C"); + v.push_back("abc"); + return v; + } + + static std::vector< std::string > replacements() + { + std::vector< std::string > v; + v.push_back("a"); + v.push_back("BBB"); + v.push_back(""); + v.push_back("123"); + return v; + } + + static std::vector< std::pair< std::string, std::string > > decorations() + { + typedef std::pair< std::string, std::string > element; + std::vector< element > v; + v.push_back(element("A", "a")); + v.push_back(element("B", "BBB")); + v.push_back(element("C", "")); + v.push_back(element("abc", "123")); + return v; + } +}; +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct test_strings< wchar_t > : public test_data< wchar_t > +{ + static const wchar_t* printable_chars() { return L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const wchar_t* escaped_chars() { return L" !\"#$%&'()*+,-./0123456789:;<=>?@aBBBDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`123defghijklmnopqrstuvwxyz{|}~"; } + + static std::vector< std::wstring > patterns() + { + std::vector< std::wstring > v; + v.push_back(L"A"); + v.push_back(L"B"); + v.push_back(L"C"); + v.push_back(L"abc"); + return v; + } + + static std::vector< std::wstring > replacements() + { + std::vector< std::wstring > v; + v.push_back(L"a"); + v.push_back(L"BBB"); + v.push_back(L""); + v.push_back(L"123"); + return v; + } + + static std::vector< std::pair< std::wstring, std::wstring > > decorations() + { + typedef std::pair< std::wstring, std::wstring > element; + std::vector< element > v; + v.push_back(element(L"A", L"a")); + v.push_back(element(L"B", L"BBB")); + v.push_back(element(L"C", L"")); + v.push_back(element(L"abc", L"123")); + return v; + } +}; +#endif + +} // namespace + +BOOST_AUTO_TEST_CASE_TEMPLATE(decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::printable_chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::char_decor(data::patterns(), data::replacements())[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << data::escaped_chars(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(zip_decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::printable_chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::char_decor(data::decorations())[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << data::escaped_chars(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_csv_decor.cpp b/src/boost/libs/log/test/run/form_csv_decor.cpp new file mode 100644 index 00000000..714a91bb --- /dev/null +++ b/src/boost/libs/log/test/run/form_csv_decor.cpp @@ -0,0 +1,84 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_csv_decor.cpp + * \author Andrey Semashev + * \date 26.09.2015 + * + * \brief This header contains tests for the \c csv_decor formatter. + */ + +#define BOOST_TEST_MODULE form_csv_decor + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions/attr.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/expressions/formatters/csv_decorator.hpp> +#include <boost/log/expressions/formatters/stream.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + +template< typename > +struct test_strings; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct test_strings< char > : public test_data< char > +{ + static const char* printable_chars() { return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const char* escaped_chars() { return " !\"\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } +}; +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct test_strings< wchar_t > : public test_data< wchar_t > +{ + static const wchar_t* printable_chars() { return L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const wchar_t* escaped_chars() { return L" !\"\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } +}; +#endif + +} // namespace + +BOOST_AUTO_TEST_CASE_TEMPLATE(decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::printable_chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::make_csv_decor< CharT >()[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << data::escaped_chars(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_date_time.cpp b/src/boost/libs/log/test/run/form_date_time.cpp new file mode 100644 index 00000000..28a3b2dd --- /dev/null +++ b/src/boost/libs/log/test/run/form_date_time.cpp @@ -0,0 +1,212 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_date_time.cpp + * \author Andrey Semashev + * \date 07.02.2009 + * + * \brief This header contains tests for the date and time formatters. + */ + +#define BOOST_TEST_MODULE form_date_time + +#include <memory> +#include <locale> +#include <string> +#include <iomanip> +#include <ostream> +#include <algorithm> +#include <boost/date_time.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/support/date_time.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; +namespace keywords = logging::keywords; + +typedef boost::posix_time::ptime ptime; +typedef boost::gregorian::date gdate; +typedef ptime::time_duration_type duration; + +namespace { + + template< typename CharT > + struct date_time_formats; + +#ifdef BOOST_LOG_USE_CHAR + template< > + struct date_time_formats< char > + { + typedef logging::basic_string_literal< char > string_literal_type; + + static string_literal_type default_date_format() { return logging::str_literal("%Y-%b-%d"); } + static string_literal_type default_time_format() { return logging::str_literal("%H:%M:%S.%f"); } + static string_literal_type default_date_time_format() { return logging::str_literal("%Y-%b-%d %H:%M:%S.%f"); } + static string_literal_type default_time_duration_format() { return logging::str_literal("%-%H:%M:%S.%f"); } + + static string_literal_type date_format() { return logging::str_literal("%d/%m/%Y"); } + static string_literal_type time_format() { return logging::str_literal("%H.%M.%S"); } + static string_literal_type date_time_format() { return logging::str_literal("%d/%m/%Y %H.%M.%S"); } + static string_literal_type time_duration_format() { return logging::str_literal("%+%H.%M.%S.%f"); } + }; +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + template< > + struct date_time_formats< wchar_t > + { + typedef logging::basic_string_literal< wchar_t > string_literal_type; + + static string_literal_type default_date_format() { return logging::str_literal(L"%Y-%b-%d"); } + static string_literal_type default_time_format() { return logging::str_literal(L"%H:%M:%S.%f"); } + static string_literal_type default_date_time_format() { return logging::str_literal(L"%Y-%b-%d %H:%M:%S.%f"); } + static string_literal_type default_time_duration_format() { return logging::str_literal(L"%-%H:%M:%S.%f"); } + + static string_literal_type date_format() { return logging::str_literal(L"%d/%m/%Y"); } + static string_literal_type time_format() { return logging::str_literal(L"%H.%M.%S"); } + static string_literal_type date_time_format() { return logging::str_literal(L"%d/%m/%Y %H.%M.%S"); } + static string_literal_type time_duration_format() { return logging::str_literal(L"%+%H.%M.%S.%f"); } + }; +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace + +// The test checks that date_time formatting work +BOOST_AUTO_TEST_CASE_TEMPLATE(date_time, CharT, char_types) +{ + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef logging::basic_formatter< CharT > formatter; + typedef test_data< CharT > data; + typedef date_time_formats< CharT > formats; + typedef boost::date_time::time_facet< ptime, CharT > facet; + + ptime t1(gdate(2009, 2, 7), ptime::time_duration_type(14, 40, 15)); + attrs::constant< ptime > attr1(t1); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + // Check for various formats specification + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_date_time< ptime >(data::attr1(), formats::default_date_time_format().c_str()); + f(rec, strm1); + strm2.imbue(std::locale(strm2.getloc(), new facet(formats::default_date_time_format().c_str()))); + strm2 << t1; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_date_time< ptime >(data::attr1(), formats::date_time_format().c_str()); + f(rec, strm1); + strm2.imbue(std::locale(strm2.getloc(), new facet(formats::date_time_format().c_str()))); + strm2 << t1; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +// The test checks that date formatting work +BOOST_AUTO_TEST_CASE_TEMPLATE(date, CharT, char_types) +{ + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef logging::basic_formatter< CharT > formatter; + typedef test_data< CharT > data; + typedef date_time_formats< CharT > formats; + typedef boost::date_time::date_facet< gdate, CharT > facet; + + gdate d1(2009, 2, 7); + attrs::constant< gdate > attr1(d1); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + // Check for various formats specification + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_date_time< gdate >(data::attr1(), formats::default_date_format().c_str()); + f(rec, strm1); + strm2.imbue(std::locale(strm2.getloc(), new facet(formats::default_date_format().c_str()))); + strm2 << d1; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_date_time< gdate >(data::attr1(), formats::date_format().c_str()); + f(rec, strm1); + strm2.imbue(std::locale(strm2.getloc(), new facet(formats::date_format().c_str()))); + strm2 << d1; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +// The test checks that time_duration formatting work +BOOST_AUTO_TEST_CASE_TEMPLATE(time_duration, CharT, char_types) +{ + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef logging::basic_formatter< CharT > formatter; + typedef test_data< CharT > data; + typedef date_time_formats< CharT > formats; + typedef boost::date_time::time_facet< ptime, CharT > facet; + + duration t1(14, 40, 15); + attrs::constant< duration > attr1(t1); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + // Check for various formats specification + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_date_time< duration >(data::attr1(), formats::default_time_duration_format().c_str()); + f(rec, strm1); + facet* fac = new facet(); + fac->time_duration_format(formats::default_time_duration_format().c_str()); + strm2.imbue(std::locale(strm2.getloc(), fac)); + strm2 << t1; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_date_time< duration >(data::attr1(), formats::time_duration_format().c_str()); + f(rec, strm1); + facet* fac = new facet(); + fac->time_duration_format(formats::time_duration_format().c_str()); + strm2.imbue(std::locale(strm2.getloc(), fac)); + strm2 << t1; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_format.cpp b/src/boost/libs/log/test/run/form_format.cpp new file mode 100644 index 00000000..af249690 --- /dev/null +++ b/src/boost/libs/log/test/run/form_format.cpp @@ -0,0 +1,83 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_format.cpp + * \author Andrey Semashev + * \date 07.02.2009 + * + * \brief This header contains tests for the Boost.Format-style formatting. + */ + +#define BOOST_TEST_MODULE form_format + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + + template< typename > + struct format_test_data; + +#ifdef BOOST_LOG_USE_CHAR + template< > + struct format_test_data< char > : + public test_data< char > + { + static const char* format1() { return "%1%, %2%"; } + }; +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + template< > + struct format_test_data< wchar_t > : + public test_data< wchar_t > + { + static const wchar_t* format1() { return L"%1%, %2%"; } + }; +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace + +// The test checks that Boost.Format formatting works +BOOST_AUTO_TEST_CASE_TEMPLATE(formatting, CharT, char_types) +{ + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef logging::basic_formatter< CharT > formatter; + typedef format_test_data< CharT > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::format(data::format1()) % expr::attr< int >(data::attr1()) % expr::attr< double >(data::attr2()); + f(rec, strm1); + strm2 << 10 << ", " << 5.5; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_if.cpp b/src/boost/libs/log/test/run/form_if.cpp new file mode 100644 index 00000000..88f44c49 --- /dev/null +++ b/src/boost/libs/log/test/run/form_if.cpp @@ -0,0 +1,107 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_if.cpp + * \author Andrey Semashev + * \date 05.02.2009 + * + * \brief This header contains tests for the \c if_ formatter. + */ + +#define BOOST_TEST_MODULE form_if + +#include <string> +#include <ostream> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that conditional formatting work +BOOST_AUTO_TEST_CASE_TEMPLATE(conditional_formatting, CharT, char_types) +{ + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef logging::basic_formatter< CharT > formatter; + typedef test_data< CharT > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + + record_view rec = make_record_view(set1); + + // Check for various modes of attribute value type specification + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << + expr::if_(expr::has_attr< int >(data::attr1())) + [ + expr::stream << expr::attr< int >(data::attr1()) + ]; + f(rec, strm1); + strm2 << 10; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1; + osstream strm1(str1); + formatter f = expr::stream << + expr::if_(expr::has_attr< int >(data::attr2())) + [ + expr::stream << expr::attr< int >(data::attr2()) + ]; + f(rec, strm1); + BOOST_CHECK(equal_strings(strm1.str(), string())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << + expr::if_(expr::has_attr< int >(data::attr1())) + [ + expr::stream << expr::attr< int >(data::attr1()) + ] + .else_ + [ + expr::stream << expr::attr< double >(data::attr2()) + ]; + f(rec, strm1); + strm2 << 10; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << + expr::if_(expr::has_attr< int >(data::attr2())) + [ + expr::stream << expr::attr< int >(data::attr1()) + ] + .else_ + [ + expr::stream << expr::attr< double >(data::attr2()) + ]; + f(rec, strm1); + strm2 << 5.5; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_max_size_decor.cpp b/src/boost/libs/log/test/run/form_max_size_decor.cpp new file mode 100644 index 00000000..772f2374 --- /dev/null +++ b/src/boost/libs/log/test/run/form_max_size_decor.cpp @@ -0,0 +1,177 @@ +/* + * Copyright Andrey Semashev 2016. + * 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 form_max_size_decor.cpp + * \author Andrey Semashev + * \date 09.08.2016 + * + * \brief This header contains tests for the \c max_size_decor formatter. + */ + +#define BOOST_TEST_MODULE form_max_size_decor + +#include <string> +#include <locale> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions/attr.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/expressions/formatters/max_size_decorator.hpp> +#include <boost/log/expressions/formatters/stream.hpp> +#include <boost/log/core/record.hpp> +#include <boost/phoenix/operator.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +#define BOOST_UTF8_DECL +#define BOOST_UTF8_BEGIN_NAMESPACE namespace { +#define BOOST_UTF8_END_NAMESPACE } + +#include <boost/detail/utf8_codecvt_facet.hpp> +#include <boost/detail/utf8_codecvt_facet.ipp> + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + +template< typename > +struct test_strings; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct test_strings< char > : public test_data< char > +{ + static const char* printable_chars() { return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const char* overflow_marker() { return ">>>"; } +}; +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct test_strings< wchar_t > : public test_data< wchar_t > +{ + static const wchar_t* printable_chars() { return L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const wchar_t* overflow_marker() { return L">>>"; } +}; +#endif + +} // namespace + +BOOST_AUTO_TEST_CASE_TEMPLATE(decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::printable_chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + // Test output truncation + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::max_size_decor< CharT >(10)[ expr::stream << expr::attr< string >(data::attr1()) << data::some_test_string() << 1234 << data::abc() ]; + f(rec, strm1); + strm2 << string(data::printable_chars(), 10); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + + // Test output truncation with a marker + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::max_size_decor(10, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) << data::some_test_string() << 1234 << data::abc() ]; + f(rec, strm1); + strm2 << string(data::printable_chars(), 7) << data::overflow_marker(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + + // Test nested decorators, when the outer decorator enforces the limit that includes the inner decorator + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::max_size_decor(35, data::overflow_marker())[ + expr::stream << data::abcdefg0123456789() << expr::max_size_decor(10, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) ] << data::abcdefg0123456789() + ]; + f(rec, strm1); + strm2 << data::abcdefg0123456789() << string(data::printable_chars(), 7) << data::overflow_marker() << string(data::abcdefg0123456789(), 5) << data::overflow_marker(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + + // Test nested decorators, when the outer decorator enforces the limit that also limits the inner decorator + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::max_size_decor(25, data::overflow_marker())[ + expr::stream << data::abcdefg0123456789() << expr::max_size_decor(10, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) ] << data::abcdefg0123456789() + ]; + f(rec, strm1); + strm2 << data::abcdefg0123456789() << string(data::printable_chars(), 5) << data::overflow_marker(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +namespace { + +const char narrow_chars[] = "\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, \xd0\xbc\xd0\xb8\xd1\x80!"; + +} // namespace + +BOOST_AUTO_TEST_CASE(character_boundary_maintenance) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< char > string; + typedef logging::basic_formatting_ostream< char > osstream; + typedef logging::basic_formatter< char > formatter; + typedef test_strings< char > data; + + std::locale loc(std::locale::classic(), new utf8_codecvt_facet()); + + attrs::constant< string > attr1(narrow_chars); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + // Test that output truncation does not happen in the middle of a multibyte character + { + string str1, str2; + osstream strm1(str1), strm2(str2); + strm1.imbue(loc); + strm2.imbue(loc); + formatter f = expr::stream << expr::max_size_decor< char >(7)[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << string(narrow_chars, 6); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + + // Test output truncation with a marker, when the marker length would have caused truncation in the middle of a multibyte character + { + string str1, str2; + osstream strm1(str1), strm2(str2); + strm1.imbue(loc); + strm2.imbue(loc); + formatter f = expr::stream << expr::max_size_decor(6, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << string(narrow_chars, 2) << data::overflow_marker(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_message.cpp b/src/boost/libs/log/test/run/form_message.cpp new file mode 100644 index 00000000..4fd946bf --- /dev/null +++ b/src/boost/libs/log/test/run/form_message.cpp @@ -0,0 +1,84 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_message.cpp + * \author Andrey Semashev + * \date 01.02.2009 + * + * \brief This header contains tests for the \c message formatter. + */ + +#define BOOST_TEST_MODULE form_message + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + + template< typename CharT > + struct message_test_data; + +#ifdef BOOST_LOG_USE_CHAR + template< > + struct message_test_data< char > : + public test_data< char > + { + static expr::smessage_type message() { return expr::smessage; } + }; +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + template< > + struct message_test_data< wchar_t > : + public test_data< wchar_t > + { + static expr::wmessage_type message() { return expr::wmessage; } + }; +#endif // BOOST_LOG_USE_WCHAR_T + +} // namespace + +// The test checks that message formatting work +BOOST_AUTO_TEST_CASE_TEMPLATE(message_formatting, CharT, char_types) +{ + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef logging::basic_formatter< CharT > formatter; + typedef message_test_data< CharT > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::message().get_name()] = attrs::constant< string >(data::some_test_string()); + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << data::message(); + f(rec, strm1); + strm2 << data::some_test_string(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_named_scope.cpp b/src/boost/libs/log/test/run/form_named_scope.cpp new file mode 100644 index 00000000..3b61e36e --- /dev/null +++ b/src/boost/libs/log/test/run/form_named_scope.cpp @@ -0,0 +1,537 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_named_scope.cpp + * \author Andrey Semashev + * \date 07.02.2009 + * + * \brief This header contains tests for the \c named_scope formatter. + */ + +#define BOOST_TEST_MODULE form_named_scope + +#include <string> +#include <boost/config.hpp> +#include <boost/preprocessor/cat.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/named_scope.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/utility/string_literal.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; +namespace keywords = logging::keywords; + +namespace { + + template< typename CharT > + struct named_scope_test_data; + + struct named_scope_test_data_base + { + static logging::string_literal scope1() { return logging::str_literal("scope1"); } + static logging::string_literal scope2() { return logging::str_literal("scope2"); } + + static logging::string_literal file() { return logging::str_literal(__FILE__); } + static logging::string_literal posix_file() { return logging::str_literal("/home/user/posix_file.cpp"); } + static logging::string_literal windows_file1() { return logging::str_literal("C:\\user\\windows_file1.cpp"); } + static logging::string_literal windows_file2() { return logging::str_literal("C:/user/windows_file2.cpp"); } + }; + +#ifdef BOOST_LOG_USE_CHAR + template< > + struct named_scope_test_data< char > : + public test_data< char >, + public named_scope_test_data_base + { + static logging::string_literal default_format() { return logging::str_literal("%n"); } + static logging::string_literal full_format() { return logging::str_literal("%n (%f:%l)"); } + static logging::string_literal short_filename_format() { return logging::str_literal("%n (%F:%l)"); } + static logging::string_literal scope_function_name_format() { return logging::str_literal("%c"); } + static logging::string_literal function_name_format() { return logging::str_literal("%C"); } + static logging::string_literal delimiter1() { return logging::str_literal("|"); } + static logging::string_literal incomplete_marker() { return logging::str_literal("<<and more>>"); } + static logging::string_literal empty_marker() { return logging::str_literal("[empty]"); } + }; +#endif // BOOST_LOG_USE_CHAR + +#ifdef BOOST_LOG_USE_WCHAR_T + template< > + struct named_scope_test_data< wchar_t > : + public test_data< wchar_t >, + public named_scope_test_data_base + { + static logging::wstring_literal default_format() { return logging::str_literal(L"%n"); } + static logging::wstring_literal full_format() { return logging::str_literal(L"%n (%f:%l)"); } + static logging::wstring_literal short_filename_format() { return logging::str_literal(L"%n (%F:%l)"); } + static logging::wstring_literal scope_function_name_format() { return logging::str_literal(L"%c"); } + static logging::wstring_literal function_name_format() { return logging::str_literal(L"%C"); } + static logging::wstring_literal delimiter1() { return logging::str_literal(L"|"); } + static logging::wstring_literal incomplete_marker() { return logging::str_literal(L"<<and more>>"); } + static logging::wstring_literal empty_marker() { return logging::str_literal(L"[empty]"); } + }; +#endif // BOOST_LOG_USE_WCHAR_T + + template< typename CharT > + inline bool check_formatting(logging::basic_string_literal< CharT > const& format, logging::record_view const& rec, std::basic_string< CharT > const& expected) + { + typedef logging::basic_formatter< CharT > formatter; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef named_scope_test_data< CharT > data; + + string str; + osstream strm(str); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), keywords::format = format.c_str()); + f(rec, strm); + return equal_strings(strm.str(), expected); + } + +} // namespace + +// The test checks that named scopes stack formatting works +BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_formatting, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + // First scope + const unsigned int line1 = __LINE__; + sentry scope1(data::scope1(), data::file(), line1); + const unsigned int line2 = __LINE__; + sentry scope2(data::scope2(), data::file(), line2); + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + // Default format + { + string str; + osstream strm(str); + strm << data::scope1() << "->" << data::scope2(); + BOOST_CHECK(check_formatting(data::default_format(), rec, strm.str())); + } + // Full format + { + string str; + osstream strm(str); + strm << data::scope1() << " (" << data::file() << ":" << line1 << ")->" + << data::scope2() << " (" << data::file() << ":" << line2 << ")"; + BOOST_CHECK(check_formatting(data::full_format(), rec, strm.str())); + } + // Different delimiter + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::delimiter = data::delimiter1().c_str()); + f(rec, strm1); + strm2 << data::scope1() << "|" << data::scope2(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + // Different direction + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::iteration = expr::reverse); + f(rec, strm1); + strm2 << data::scope2() << "<-" << data::scope1(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::delimiter = data::delimiter1().c_str(), + keywords::iteration = expr::reverse); + f(rec, strm1); + strm2 << data::scope2() << "|" << data::scope1(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + // Limiting the number of scopes + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::depth = 1); + f(rec, strm1); + strm2 << "..." << data::scope2(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::depth = 1, + keywords::iteration = expr::reverse); + f(rec, strm1); + strm2 << data::scope2() << "..."; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::delimiter = data::delimiter1().c_str(), + keywords::depth = 1); + f(rec, strm1); + strm2 << "..." << data::scope2(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::delimiter = data::delimiter1().c_str(), + keywords::depth = 1, + keywords::iteration = expr::reverse); + f(rec, strm1); + strm2 << data::scope2() << "..."; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::incomplete_marker = data::incomplete_marker().c_str(), + keywords::depth = 1); + f(rec, strm1); + strm2 << "<<and more>>" << data::scope2(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::incomplete_marker = data::incomplete_marker().c_str(), + keywords::depth = 1, + keywords::iteration = expr::reverse); + f(rec, strm1); + strm2 << data::scope2() << "<<and more>>"; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +// The test checks that empty named scopes stack formatting works +BOOST_AUTO_TEST_CASE_TEMPLATE(empty_scopes_formatting, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + formatter f = expr::stream << expr::format_named_scope(data::attr1(), + keywords::format = data::default_format().c_str(), + keywords::empty_marker = data::empty_marker().c_str()); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << "[empty]"; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + + const unsigned int line1 = __LINE__; + sentry scope1(data::scope1(), data::file(), line1); + const unsigned int line2 = __LINE__; + sentry scope2(data::scope2(), data::file(), line2); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << data::scope1() << "->" << data::scope2(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_filename_formatting_posix, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + // First scope + const unsigned int line1 = __LINE__; + sentry scope1(data::scope1(), data::posix_file(), line1); + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + // File names without the full path + { + string str; + osstream strm(str); + strm << data::scope1() << " (posix_file.cpp:" << line1 << ")"; + BOOST_CHECK(check_formatting(data::short_filename_format(), rec, strm.str())); + } +} + +#if defined(BOOST_WINDOWS) + +BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_filename_formatting_windows, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + // First scope + const unsigned int line1 = __LINE__; + sentry scope1(data::scope1(), data::windows_file1(), line1); + const unsigned int line2 = __LINE__; + sentry scope2(data::scope2(), data::windows_file2(), line2); + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + // File names without the full path + { + string str; + osstream strm(str); + strm << data::scope1() << " (windows_file1.cpp:" << line1 << ")->" + << data::scope2() << " (windows_file2.cpp:" << line2 << ")"; + BOOST_CHECK(check_formatting(data::short_filename_format(), rec, strm.str())); + } +} + +#endif // defined(BOOST_WINDOWS) + +namespace { + +struct named_scope_test_case +{ + logging::string_literal scope_name; + const char* function_name; + const char* function_name_no_scope; +}; + +const named_scope_test_case named_scope_test_cases[] = +{ + // Generic signatures + { logging::str_literal("int main(int, char *[])"), "main", "main" }, + { logging::str_literal("namespace_name::type foo()"), "foo", "foo" }, + { logging::str_literal("namespace_name::type& foo::bar(int[], std::string const&)"), "foo::bar", "bar" }, + { logging::str_literal("void* namespc::foo<char>::bar()"), "namespc::foo<char>::bar", "bar" }, + { logging::str_literal("void* namespc::foo<char>::bar<int>(int) const"), "namespc::foo<char>::bar<int>", "bar<int>" }, + + // MSVC-specific + { logging::str_literal("int __cdecl main(int, char *[])"), "main", "main" }, + { logging::str_literal("struct namespc::strooct __cdecl foo3(int [])"), "foo3", "foo3" }, + { logging::str_literal("void (__cdecl *__cdecl foo4(void))(void)"), "foo4", "foo4" }, // function returning pointer to function + { logging::str_literal("void (__cdecl *__cdecl foo5(void (__cdecl *)(void)))(void)"), "foo5", "foo5" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<int>::member1(void (__cdecl *)(void)))(void)"), "namespc::my_class<int>::member1", "member1" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<int>::member2<int>(int))(void)"), "namespc::my_class<int>::member2<int>", "member2<int>" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<int>::member2<void(__cdecl *)(void)>(void (__cdecl *)(void)))(void)"), "namespc::my_class<int>::member2<void(__cdecl *)(void)>", "member2<void(__cdecl *)(void)>" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<int>::member3<void __cdecl foo1(void)>(void))(void)"), "namespc::my_class<int>::member3<void __cdecl foo1(void)>", "member3<void __cdecl foo1(void)>" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<void (__cdecl*)(void)>::member1(void (__cdecl *)(void)))(void)"), "namespc::my_class<void (__cdecl*)(void)>::member1", "member1" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<void (__cdecl*)(void)>::member2<int>(int))(void)"), "namespc::my_class<void (__cdecl*)(void)>::member2<int>", "member2<int>" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<void (__cdecl*)(void)>::member2<void(__cdecl *)(void)>(void (__cdecl *)(void)))(void)"), "namespc::my_class<void (__cdecl*)(void)>::member2<void(__cdecl *)(void)>", "member2<void(__cdecl *)(void)>" }, + { logging::str_literal("void (__cdecl *__cdecl namespc::my_class<void (__cdecl*)(void)>::member3<void __cdecl foo1(void)>(void))(void)"), "namespc::my_class<void (__cdecl*)(void)>::member3<void __cdecl foo1(void)>", "member3<void __cdecl foo1(void)>" }, + { logging::str_literal("void (__cdecl namespc::my_class2::* __cdecl namespc::foo6(void (__cdecl *)(void)))(void)"), "namespc::foo6", "foo6" }, + { logging::str_literal("struct namespc::my_class<void __cdecl(int)> __cdecl namespc::foo7(void)"), "namespc::foo7", "foo7" }, + { logging::str_literal("void (__cdecl namespc::my_class2::* const (&__cdecl namespc::foo8(void (__cdecl *)(void)))[2])(void)"), "namespc::foo8", "foo8" }, + { logging::str_literal("__cdecl namespc::my_class2::my_class2(void)"), "namespc::my_class2::my_class2", "my_class2" }, + { logging::str_literal("__cdecl namespc::my_class2::~my_class2(void)"), "namespc::my_class2::~my_class2", "~my_class2" }, + { logging::str_literal("void __cdecl namespc::my_class2::operator =(const struct namespc::my_class2 &)"), "namespc::my_class2::operator =", "operator =" }, + { logging::str_literal("void __cdecl namespc::my_class2::operator *(void) const"), "namespc::my_class2::operator *", "operator *" }, + { logging::str_literal("void __cdecl namespc::my_class2::operator ()(void)"), "namespc::my_class2::operator ()", "operator ()" }, + { logging::str_literal("bool __cdecl namespc::my_class2::operator <(int) const"), "namespc::my_class2::operator <", "operator <" }, + { logging::str_literal("bool __cdecl namespc::my_class2::operator >(int) const"), "namespc::my_class2::operator >", "operator >" }, + { logging::str_literal("bool __cdecl namespc::my_class2::operator <=(int) const"), "namespc::my_class2::operator <=", "operator <=" }, + { logging::str_literal("bool __cdecl namespc::my_class2::operator >=(int) const"), "namespc::my_class2::operator >=", "operator >=" }, + { logging::str_literal("__cdecl namespc::my_class2::operator bool(void) const"), "namespc::my_class2::operator bool", "operator bool" }, + // MSVC generates incorrect strings in case of conversion operators to function types. We don't support these. +// { logging::str_literal("__cdecl namespc::my_class2::operator char (__cdecl *)(double)(__cdecl *(void) const)(double)"), "namespc::my_class2::operator char (__cdecl *)(double)", "operator char (__cdecl *)(double)" }, +// { logging::str_literal("__cdecl namespc::my_class2::operator char (__cdecl namespc::my_class2::* )(double)(__cdecl namespc::my_class2::* (void) const)(double)"), "namespc::my_class2::operator char (__cdecl namespc::my_class2::* )(double)", "operator char (__cdecl namespc::my_class2::* )(double)" }, + { logging::str_literal("class std::basic_ostream<char,struct std::char_traits<char> > &__cdecl namespc::operator <<<char,struct std::char_traits<char>>(class std::basic_ostream<char,struct std::char_traits<char> > &,const struct namespc::my_class2 &)"), "namespc::operator <<<char,struct std::char_traits<char>>", "operator <<<char,struct std::char_traits<char>>" }, + { logging::str_literal("class std::basic_istream<char,struct std::char_traits<char> > &__cdecl namespc::operator >><char,struct std::char_traits<char>>(class std::basic_istream<char,struct std::char_traits<char> > &,struct namespc::my_class2 &)"), "namespc::operator >><char,struct std::char_traits<char>>", "operator >><char,struct std::char_traits<char>>" }, + + // GCC-specific + { logging::str_literal("namespc::strooct foo3(int*)"), "foo3", "foo3" }, + { logging::str_literal("void (* foo4())()"), "foo4", "foo4" }, // function returning pointer to function + { logging::str_literal("void (* foo5(pfun2_t))()"), "foo5", "foo5" }, + { logging::str_literal("static void (* namespc::my_class<T>::member1(pfun2_t))() [with T = int; pfun1_t = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class<T>::member1", "member1" }, + { logging::str_literal("static void (* namespc::my_class<T>::member2(U))() [with U = int; T = int; pfun2_t = void (*)()]"), "namespc::my_class<T>::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class<T>::member2(U))() [with U = void (*)(); T = int; pfun2_t = void (*)()]"), "namespc::my_class<T>::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class<T>::member3())() [with void (* Fun)() = foo1; T = int; pfun2_t = void (*)()]"), "namespc::my_class<T>::member3", "member3" }, + { logging::str_literal("static void (* namespc::my_class<T>::member1(pfun2_t))() [with T = void (*)(); pfun1_t = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class<T>::member1", "member1" }, + { logging::str_literal("static void (* namespc::my_class<T>::member2(U))() [with U = int; T = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class<T>::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class<T>::member2(U))() [with U = void (*)(); T = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class<T>::member2", "member2" }, + { logging::str_literal("static void (* namespc::my_class<T>::member3())() [with void (* Fun)() = foo1; T = void (*)(); pfun2_t = void (*)()]"), "namespc::my_class<T>::member3", "member3" }, + { logging::str_literal("void (namespc::my_class2::* namespc::foo6(pfun2_t))()"), "namespc::foo6", "foo6" }, + { logging::str_literal("namespc::my_class<void(int)> namespc::foo7()"), "namespc::foo7", "foo7" }, + { logging::str_literal("void (namespc::my_class2::* const (& namespc::foo8(pfun2_t))[2])()"), "namespc::foo8", "foo8" }, + { logging::str_literal("namespc::my_class2::my_class2()"), "namespc::my_class2::my_class2", "my_class2" }, // constructor + { logging::str_literal("namespc::my_class2::~my_class2()"), "namespc::my_class2::~my_class2", "~my_class2" }, // destructor + { logging::str_literal("void namespc::my_class2::operator=(const namespc::my_class2&)"), "namespc::my_class2::operator=", "operator=" }, + { logging::str_literal("void namespc::my_class2::operator*() const"), "namespc::my_class2::operator*", "operator*" }, + { logging::str_literal("void namespc::my_class2::operator()()"), "namespc::my_class2::operator()", "operator()" }, + { logging::str_literal("bool namespc::my_class2::operator<(int) const"), "namespc::my_class2::operator<", "operator<" }, + { logging::str_literal("bool namespc::my_class2::operator>(int) const"), "namespc::my_class2::operator>", "operator>" }, + { logging::str_literal("bool namespc::my_class2::operator<=(int) const"), "namespc::my_class2::operator<=", "operator<=" }, + { logging::str_literal("bool namespc::my_class2::operator>=(int) const"), "namespc::my_class2::operator>=", "operator>=" }, + { logging::str_literal("namespc::my_class2::operator bool() const"), "namespc::my_class2::operator bool", "operator bool" }, + { logging::str_literal("namespc::my_class2::operator pfun1_t() const"), "namespc::my_class2::operator pfun1_t", "operator pfun1_t" }, + { logging::str_literal("std::basic_ostream<_CharT, _Traits>& namespc::operator<<(std::basic_ostream<_CharT, _Traits>&, const namespc::my_class2&) [with CharT = char; TraitsT = std::char_traits<char>]"), "namespc::operator<<", "operator<<" }, + { logging::str_literal("std::basic_istream<_CharT, _Traits>& namespc::operator>>(std::basic_istream<_CharT, _Traits>&, namespc::my_class2&) [with CharT = char; TraitsT = std::char_traits<char>]"), "namespc::operator>>", "operator>>" }, + + // BOOST_CURRENT_FUNCTION fallback value + { logging::str_literal("(unknown)"), "(unknown)", "(unknown)" } +}; + +} // namespace + +// Function name formatting +BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_scope_function_name_formatting, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + // First scope + const unsigned int line1 = __LINE__; + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + for (unsigned int i = 0; i < sizeof(named_scope_test_cases) / sizeof(*named_scope_test_cases); ++i) + { + sentry scope1(named_scope_test_cases[i].scope_name, data::file(), line1, attrs::named_scope_entry::function); + string str; + osstream strm(str); + strm << named_scope_test_cases[i].function_name; + BOOST_CHECK_MESSAGE(check_formatting(data::scope_function_name_format(), rec, strm.str()), "Scope name: " << named_scope_test_cases[i].scope_name); + } +} + +// Function name without scope formatting +BOOST_AUTO_TEST_CASE_TEMPLATE(scopes_function_name_formatting, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + typedef named_scope::sentry sentry; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + // First scope + const unsigned int line1 = __LINE__; + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + for (unsigned int i = 0; i < sizeof(named_scope_test_cases) / sizeof(*named_scope_test_cases); ++i) + { + sentry scope1(named_scope_test_cases[i].scope_name, data::file(), line1, attrs::named_scope_entry::function); + string str; + osstream strm(str); + strm << named_scope_test_cases[i].function_name_no_scope; + BOOST_CHECK_MESSAGE(check_formatting(data::function_name_format(), rec, strm.str()), "Scope name: " << named_scope_test_cases[i].scope_name); + } +} + +// The test checks that function name formatters do not affect scopes denoted with BOOST_LOG_NAMED_SCOPE +BOOST_AUTO_TEST_CASE_TEMPLATE(function_name_does_not_affect_non_function_scopes, CharT, char_types) +{ + typedef attrs::named_scope named_scope; + + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::record_view record_view; + typedef named_scope_test_data< CharT > data; + + named_scope attr; + + attr_set set1; + set1[data::attr1()] = attr; + + record_view rec = make_record_view(set1); + + { + BOOST_LOG_NAMED_SCOPE("void foo()"); + string str; + osstream strm(str); + strm << "void foo()"; + BOOST_CHECK(check_formatting(data::scope_function_name_format(), rec, strm.str())); + BOOST_CHECK(check_formatting(data::function_name_format(), rec, strm.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_to_log_manip.cpp b/src/boost/libs/log/test/run/form_to_log_manip.cpp new file mode 100644 index 00000000..a7f83d36 --- /dev/null +++ b/src/boost/libs/log/test/run/form_to_log_manip.cpp @@ -0,0 +1,98 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_to_log_manip.cpp + * \author Andrey Semashev + * \date 05.07.2015 + * + * \brief This header contains tests for support for the \c to_log_manip customization point. + */ + +#define BOOST_TEST_MODULE form_to_log_manip + +#include <string> +#include <ostream> +#include <algorithm> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/utility/manipulators/to_log.hpp> +#include <boost/log/expressions.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + +struct my_class +{ + int m_Data; + + explicit my_class(int data) : m_Data(data) {} +}; + +} // namespace + +BOOST_LOG_ATTRIBUTE_KEYWORD(a_my_class, "MyClass", my_class) +BOOST_LOG_ATTRIBUTE_KEYWORD(a_string, "String", std::string) + +namespace { + +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& +operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, logging::to_log_manip< my_class, tag::a_my_class > const& obj) +{ + strm << "a_my_class: [data: " << obj.get().m_Data << "]"; + return strm; +} + +} // namespace + +namespace std { + +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& +operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, logging::to_log_manip< std::string, tag::a_string > const& obj) +{ + strm << "a_string: [" << obj.get() << "]"; + return strm; +} + +} // namespace std + +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overrides, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + + attrs::constant< my_class > attr1(my_class(77)); + attrs::constant< std::string > attr2("Hello"); + + attr_set set1; + set1[a_my_class.get_name()] = attr1; + set1[a_string.get_name()] = attr2; + + record_view rec = make_record_view(set1); + + // Check that out custom operators are called + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << a_my_class << ", " << a_string; + f(rec, strm1); + strm2 << "a_my_class: [data: " << 77 << "], a_string: [" << "Hello" << "]"; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/form_xml_decor.cpp b/src/boost/libs/log/test/run/form_xml_decor.cpp new file mode 100644 index 00000000..416c9c4e --- /dev/null +++ b/src/boost/libs/log/test/run/form_xml_decor.cpp @@ -0,0 +1,84 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 form_xml_decor.cpp + * \author Andrey Semashev + * \date 26.09.2015 + * + * \brief This header contains tests for the \c xml_decor formatter. + */ + +#define BOOST_TEST_MODULE form_xml_decor + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/utility/type_dispatch/standard_types.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions/attr.hpp> +#include <boost/log/expressions/formatter.hpp> +#include <boost/log/expressions/formatters/xml_decorator.hpp> +#include <boost/log/expressions/formatters/stream.hpp> +#include <boost/log/core/record.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + +template< typename > +struct test_strings; + +#ifdef BOOST_LOG_USE_CHAR +template< > +struct test_strings< char > : public test_data< char > +{ + static const char* printable_chars() { return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const char* escaped_chars() { return " !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } +}; +#endif + +#ifdef BOOST_LOG_USE_WCHAR_T +template< > +struct test_strings< wchar_t > : public test_data< wchar_t > +{ + static const wchar_t* printable_chars() { return L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } + static const wchar_t* escaped_chars() { return L" !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; } +}; +#endif + +} // namespace + +BOOST_AUTO_TEST_CASE_TEMPLATE(decorator_formatting, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_strings< CharT > data; + + attrs::constant< string > attr1(data::printable_chars()); + + attr_set set1; + set1[data::attr1()] = attr1; + + record_view rec = make_record_view(set1); + + { + string str1, str2; + osstream strm1(str1), strm2(str2); + formatter f = expr::stream << expr::make_xml_decor< CharT >()[ expr::stream << expr::attr< string >(data::attr1()) ]; + f(rec, strm1); + strm2 << data::escaped_chars(); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/src/boost/libs/log/test/run/sink_text_ipc_mq_backend.cpp b/src/boost/libs/log/test/run/sink_text_ipc_mq_backend.cpp new file mode 100644 index 00000000..e26f53a8 --- /dev/null +++ b/src/boost/libs/log/test/run/sink_text_ipc_mq_backend.cpp @@ -0,0 +1,67 @@ +/* + * Copyright Lingxi Li 2015. + * Copyright Andrey Semashev 2016. + * 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 sink_text_ipc_mq_backend.cpp + * \author Lingxi Li + * \author Andrey Semashev + * \date 19.10.2015 + * + * \brief The test verifies that \c text_ipc_message_queue_backend works as expected. + */ + +#if !defined(BOOST_LOG_WITHOUT_IPC) + +#define BOOST_TEST_MODULE sink_text_ipc_mq_backend + +#include <boost/log/sinks/text_ipc_message_queue_backend.hpp> +#include <boost/log/utility/ipc/reliable_message_queue.hpp> +#include <boost/log/utility/ipc/object_name.hpp> +#include <boost/log/utility/open_mode.hpp> +#include <boost/log/core/record_view.hpp> +#include <boost/test/unit_test.hpp> +#include <string> +#include "make_record.hpp" +#include "char_definitions.hpp" + +const boost::log::ipc::object_name ipc_queue_name(boost::log::ipc::object_name::session, "boost_log_test_text_ipc_mq_backend"); +const unsigned int capacity = 512; +const unsigned int block_size = 1024; +const char message[] = "Hello, world!"; + +// The test checks that `text_ipc_message_queue_backend` works. +BOOST_AUTO_TEST_CASE(text_ipc_message_queue_backend) +{ + typedef boost::log::ipc::reliable_message_queue queue_t; + typedef boost::log::sinks::text_ipc_message_queue_backend< queue_t > backend_t; + + // Do a remove in case if a previous test failed + queue_t::remove(ipc_queue_name); + + backend_t backend; + BOOST_CHECK(!backend.is_open()); + + backend.message_queue().create(ipc_queue_name, capacity, block_size); + BOOST_CHECK(backend.is_open()); + + queue_t queue(boost::log::open_mode::open_only, ipc_queue_name); + boost::log::record_view rec = make_record_view(); + backend.consume(rec, message); + + std::string msg; + BOOST_CHECK(queue.try_receive(msg)); + BOOST_CHECK(equal_strings(msg, message)); +} + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/src/boost/libs/log/test/run/src_record_ostream.cpp b/src/boost/libs/log/test/run/src_record_ostream.cpp new file mode 100644 index 00000000..dd321f92 --- /dev/null +++ b/src/boost/libs/log/test/run/src_record_ostream.cpp @@ -0,0 +1,479 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 src_record_ostream.cpp + * \author Andrey Semashev + * \date 23.08.2015 + * + * \brief This header contains tests for the log record formatting output stream. + */ + +#define BOOST_TEST_MODULE src_record_ostream + +#include <locale> +#include <string> +#include <iomanip> +#include <sstream> +#include <iostream> +#include <algorithm> +#include <boost/config.hpp> +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +#include <string_view> +#endif +#include <boost/test/unit_test.hpp> +#include <boost/utility/string_view.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/sources/record_ostream.hpp> +#include <boost/log/expressions/message.hpp> +#include <boost/log/attributes/value_extraction.hpp> +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace expr = boost::log::expressions; + +namespace { + +struct unreferencable_data +{ + unsigned int m : 2; + unsigned int n : 6; + + enum my_enum + { + one = 1, + two = 2 + }; + + // The following static constants don't have definitions, so they can only be used in constant expressions. + // Trying to bind a reference to these members will result in linking errors. + static const int x = 7; + static const my_enum y = one; + + unreferencable_data() + { + m = 1; + n = 5; + } +}; + +template< typename CharT > +struct test_impl +{ + typedef CharT char_type; + typedef test_data< char_type > strings; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_record_ostream< char_type > record_ostream_type; + + template< typename StringT > + static void width_formatting() + { + // Check that widening works + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + strm_fmt << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + + // Check that the string is not truncated + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + strm_fmt << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + } + + template< typename StringT > + static void fill_formatting() + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + strm_fmt << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + + template< typename StringT > + static void alignment() + { + // Left alignment + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + strm_fmt << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + + // Right alignment + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + strm_fmt << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template< typename StringT > + static void rvalue_stream() + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type(rec) << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC() << std::flush; + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } +#endif + + static void output_unreferencable_data() + { + unreferencable_data data; + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + strm_fmt << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y; + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << static_cast< unsigned int >(data.m) << static_cast< char_type >(' ') << static_cast< unsigned int >(data.n) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::x) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::y); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type(rec) << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y << std::flush; + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << static_cast< unsigned int >(data.m) << static_cast< char_type >(' ') << static_cast< unsigned int >(data.n) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::x) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::y); + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } +#endif + } + + static void formatting_params_restoring() + { + record_ostream_type strm_fmt; + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + strm_fmt.attach_record(rec); + strm_fmt << std::setw(8) << std::setfill(static_cast< char_type >('x')) << std::hex << 15; + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + strm_fmt.detach_from_record(); + + ostream_type strm_correct; + strm_correct << std::setw(8) << std::setfill(static_cast< char_type >('x')) << std::hex << 15; + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + + // Check that the formatting flags are reset for the next record + { + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + strm_fmt.attach_record(rec); + strm_fmt << 15; + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + strm_fmt.detach_from_record(); + + ostream_type strm_correct; + strm_correct << 15; + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); + } + } +}; + +} // namespace + +// Test support for width formatting +BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >(); + test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >(); +#endif +} + +// Test support for filler character setup +BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >(); + test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >(); +#endif +} + +// Test support for text alignment +BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE alignment< const CharT* >(); + test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >(); +#endif +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +// Test support for rvalue stream objects +BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >(); + test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >(); +#endif +} +#endif + +// Test output of data to which a reference cannot be bound +BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::output_unreferencable_data(); +} + +// Test that formatting settings are reset for new log records +BOOST_AUTO_TEST_CASE_TEMPLATE(formatting_params_restoring, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::formatting_params_restoring(); +} + +namespace my_namespace { + +class A {}; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, A const&) +{ + strm << "A"; + return strm; +} + +class B {}; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B&) +{ + strm << "B"; + return strm; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*) +{ + strm << "B*"; + return strm; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*) +{ + strm << "const B*"; + return strm; +} + +class C {}; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&) +{ + strm << "C"; + return strm; +} + +enum E { eee }; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, E) +{ + strm << "E"; + return strm; +} + +} // namespace my_namespace + +// Test operator forwarding +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_record_ostream< char_type > record_ostream_type; + + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + + const my_namespace::A a = my_namespace::A(); // const lvalue + my_namespace::B b; // lvalue + strm_fmt << a << b << my_namespace::C(); // rvalue + strm_fmt << my_namespace::eee; + strm_fmt << &b << (my_namespace::B const*)&b; + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b; + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); +} + +namespace my_namespace2 { + +class A {}; +template< typename CharT > +inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, A const&) +{ + strm << "A"; + return strm; +} + +class B {}; +template< typename CharT > +inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, B&) +{ + strm << "B"; + return strm; +} + +class C {}; +template< typename CharT > +inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, C const&) +{ + strm << "C"; + return strm; +} + +class D {}; +template< typename CharT > +inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + D&& +#else + D const& +#endif + ) +{ + strm << "D"; + return strm; +} + +enum E { eee }; +template< typename CharT > +inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, E) +{ + strm << "E"; + return strm; +} + +} // namespace my_namespace2 + +// Test operator overriding +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_record_ostream< char_type > record_ostream_type; + + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + + const my_namespace2::A a = my_namespace2::A(); // const lvalue + my_namespace2::B b; // lvalue + strm_fmt << a << b << my_namespace2::C() << my_namespace2::D(); // rvalue + strm_fmt << my_namespace2::eee; + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << "ABCDE"; + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); +} + +// Test that operator<< returns a record_ostream +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_return_type, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_record_ostream< char_type > record_ostream_type; + + logging::record rec = make_record(); + BOOST_REQUIRE(!!rec); + record_ostream_type strm_fmt(rec); + + // The test here verifies that the result of << "Hello" is a record_ostream, not std::ostream or logging::formatting_ostream. + // The subsequent << A() will only compile if the stream is record_ostream. + strm_fmt << "Hello " << my_namespace2::A(); + strm_fmt.flush(); + string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); + + ostream_type strm_correct; + strm_correct << "Hello A"; + + BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); +} diff --git a/src/boost/libs/log/test/run/util_dynamic_type_disp.cpp b/src/boost/libs/log/test/run/util_dynamic_type_disp.cpp new file mode 100644 index 00000000..e64abb1d --- /dev/null +++ b/src/boost/libs/log/test/run/util_dynamic_type_disp.cpp @@ -0,0 +1,138 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_dynamic_type_disp.cpp + * \author Andrey Semashev + * \date 09.01.2009 + * + * \brief This header contains tests for the dynamic type dispatcher. + */ + +#define BOOST_TEST_MODULE util_dynamic_type_disp + +#include <string> +#include <boost/bind.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/tools/floating_point_comparison.hpp> +#include <boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp> + +namespace logging = boost::log; + +namespace { + + // A simple attribute value + template< typename T > + struct my_value + { + T m_Value; + + explicit my_value(T const& value) : m_Value(value) {} + + // The function passes the contained type into the dispatcher + bool dispatch(logging::type_dispatcher& dispatcher) + { + logging::type_dispatcher::callback< T > callback = dispatcher.get_callback< T >(); + if (callback) + { + callback(m_Value); + return true; + } + else + return false; + } + }; + + struct my_visitor + { + enum type_expected + { + none_expected, + int_expected, + double_expected, + string_expected + }; + + my_visitor() : m_Expected(none_expected), m_Int(0), m_Double(0.0) {} + + void set_expected() + { + m_Expected = none_expected; + } + void set_expected(int value) + { + m_Expected = int_expected; + m_Int = value; + } + void set_expected(double value) + { + m_Expected = double_expected; + m_Double = value; + } + void set_expected(std::string const& value) + { + m_Expected = string_expected; + m_String = value; + } + + // Implement visitation logic for all supported types + void on_int(int const& value) + { + BOOST_CHECK_EQUAL(m_Expected, int_expected); + BOOST_CHECK_EQUAL(m_Int, value); + } + void on_double(double const& value) + { + BOOST_CHECK_EQUAL(m_Expected, double_expected); + BOOST_CHECK_CLOSE(m_Double, value, 0.001); + } + void on_string(std::string const& value) + { + BOOST_CHECK_EQUAL(m_Expected, string_expected); + BOOST_CHECK_EQUAL(m_String, value); + } + + private: + type_expected m_Expected; + int m_Int; + double m_Double; + std::string m_String; + }; + +} // namespace + +// The test checks that general functionality works +BOOST_AUTO_TEST_CASE(type_dispatch) +{ + my_visitor vis; + logging::dynamic_type_dispatcher disp; + + // Register type visitors + disp.register_type< int >(boost::bind(&my_visitor::on_int, &vis, _1)); + disp.register_type< double >(boost::bind(&my_visitor::on_double, &vis, _1)); + + BOOST_CHECK(disp.registered_types_count() == 2); + + // Right now strings are not supported by the dispatcher + my_value< std::string > val1("Hello world!"); + BOOST_CHECK(!val1.dispatch(disp)); + + // And now they are + disp.register_type< std::string >(boost::bind(&my_visitor::on_string, &vis, _1)); + BOOST_CHECK(disp.registered_types_count() == 3); + + vis.set_expected(val1.m_Value); + BOOST_CHECK(val1.dispatch(disp)); + + my_value< double > val2(1.2); + vis.set_expected(val2.m_Value); + BOOST_CHECK(val2.dispatch(disp)); + + // This one is not supported + my_value< float > val3(static_cast< float >(-4.3)); + vis.set_expected(); + BOOST_CHECK(!val3.dispatch(disp)); +} diff --git a/src/boost/libs/log/test/run/util_exception_handler.cpp b/src/boost/libs/log/test/run/util_exception_handler.cpp new file mode 100644 index 00000000..1bdb5b41 --- /dev/null +++ b/src/boost/libs/log/test/run/util_exception_handler.cpp @@ -0,0 +1,361 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_exception_handler.cpp + * \author Andrey Semashev + * \date 13.07.2009 + * + * \brief This header contains tests for the exception handler functional objects. + */ + +#define BOOST_TEST_MODULE util_exception_handler + +#include <string> +#include <typeinfo> +#include <stdexcept> +#include <boost/mpl/vector.hpp> +#include <boost/smart_ptr/scoped_ptr.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/exception_handler.hpp> + +namespace logging = boost::log; + +namespace { + + struct my_handler1 + { + typedef void result_type; + + std::type_info const*& m_pExceptionType; + + my_handler1(std::type_info const*& p) : m_pExceptionType(p) {} + + void operator() (std::exception&) const + { + m_pExceptionType = &typeid(std::exception); + } + void operator() (std::runtime_error&) const + { + m_pExceptionType = &typeid(std::runtime_error); + } + }; + + struct my_handler2 + { + typedef void result_type; + typedef boost::mpl::vector< std::runtime_error, std::exception >::type exception_types; + + std::type_info const*& m_pExceptionType; + + explicit my_handler2(std::type_info const*& p) : m_pExceptionType(p) {} + + void operator() (std::exception&) const + { + m_pExceptionType = &typeid(std::exception); + } + void operator() (std::runtime_error&) const + { + m_pExceptionType = &typeid(std::runtime_error); + } + }; + + struct my_handler1_nothrow + { + typedef void result_type; + + std::type_info const*& m_pExceptionType; + + my_handler1_nothrow(std::type_info const*& p) : m_pExceptionType(p) {} + + void operator() (std::exception&) const + { + m_pExceptionType = &typeid(std::exception); + } + void operator() (std::runtime_error&) const + { + m_pExceptionType = &typeid(std::runtime_error); + } + void operator() () const + { + m_pExceptionType = &typeid(void); + } + }; + + struct my_handler2_nothrow + { + typedef void result_type; + typedef boost::mpl::vector< std::runtime_error, std::exception >::type exception_types; + + std::type_info const*& m_pExceptionType; + + explicit my_handler2_nothrow(std::type_info const*& p) : m_pExceptionType(p) {} + + void operator() (std::exception&) const + { + m_pExceptionType = &typeid(std::exception); + } + void operator() (std::runtime_error&) const + { + m_pExceptionType = &typeid(std::runtime_error); + } + void operator() () const + { + m_pExceptionType = &typeid(void); + } + }; + + struct my_exception {}; + + struct my_function0 + { + struct impl_base + { + virtual ~impl_base() {} + virtual void invoke() = 0; + }; + + template< typename T > + struct impl : public impl_base + { + T m_Fun; + + explicit impl(T const& fun) : m_Fun(fun) {} + void invoke() { m_Fun(); } + }; + + private: + boost::scoped_ptr< impl_base > m_pImpl; + + public: + template< typename T > + my_function0& operator= (T const& fun) + { + m_pImpl.reset(new impl< T >(fun)); + return *this; + } + + void operator() () const + { + m_pImpl->invoke(); + } + }; + +} // namespace + +// Tests for handler with explicit exception types specification +BOOST_AUTO_TEST_CASE(explicit_exception_types) +{ + std::type_info const* pExceptionType = 0; + my_function0 handler; + handler = logging::make_exception_handler< + std::runtime_error, + std::exception + >(my_handler1(pExceptionType)); + + try + { + throw std::runtime_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::runtime_error)); + pExceptionType = 0; + + try + { + throw std::logic_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::exception)); + pExceptionType = 0; + + try + { + throw my_exception(); + } + catch (...) + { + BOOST_CHECK_THROW(handler(), my_exception); + } + BOOST_REQUIRE(pExceptionType == 0); + + // Verify that exception types are checked in the specified order + handler = logging::make_exception_handler< + std::exception, + std::runtime_error + >(my_handler1(pExceptionType)); + + try + { + throw std::runtime_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::exception)); + pExceptionType = 0; +} + +// Tests for handler with explicit exception types specification (no-throw version) +BOOST_AUTO_TEST_CASE(explicit_exception_types_nothrow) +{ + std::type_info const* pExceptionType = 0; + my_function0 handler; + handler = logging::make_exception_handler< + std::runtime_error, + std::exception + >(my_handler1_nothrow(pExceptionType), std::nothrow); + + try + { + throw std::runtime_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::runtime_error)); + pExceptionType = 0; + + try + { + throw std::logic_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::exception)); + pExceptionType = 0; + + try + { + throw my_exception(); + } + catch (...) + { + BOOST_CHECK_NO_THROW(handler()); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(void)); + pExceptionType = 0; + + // Verify that exception types are checked in the specified order + handler = logging::make_exception_handler< + std::exception, + std::runtime_error + >(my_handler1_nothrow(pExceptionType), std::nothrow); + + try + { + throw std::runtime_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::exception)); + pExceptionType = 0; +} + +// Tests for handler with self-contained exception types +BOOST_AUTO_TEST_CASE(self_contained_exception_types) +{ + std::type_info const* pExceptionType = 0; + my_function0 handler; + handler = logging::make_exception_handler(my_handler2(pExceptionType)); + + try + { + throw std::runtime_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::runtime_error)); + pExceptionType = 0; + + try + { + throw std::logic_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::exception)); + pExceptionType = 0; + + try + { + throw my_exception(); + } + catch (...) + { + BOOST_CHECK_THROW(handler(), my_exception); + } + BOOST_REQUIRE(pExceptionType == 0); +} + +// Tests for handler with self-contained exception types (no-throw version) +BOOST_AUTO_TEST_CASE(self_contained_exception_types_nothrow) +{ + std::type_info const* pExceptionType = 0; + my_function0 handler; + handler = logging::make_exception_handler(my_handler2_nothrow(pExceptionType), std::nothrow); + + try + { + throw std::runtime_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::runtime_error)); + pExceptionType = 0; + + try + { + throw std::logic_error("error"); + } + catch (...) + { + handler(); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(std::exception)); + pExceptionType = 0; + + try + { + throw my_exception(); + } + catch (...) + { + BOOST_CHECK_NO_THROW(handler()); + } + BOOST_REQUIRE(pExceptionType != 0); + BOOST_CHECK(*pExceptionType == typeid(void)); + pExceptionType = 0; +} diff --git a/src/boost/libs/log/test/run/util_formatting_ostream.cpp b/src/boost/libs/log/test/run/util_formatting_ostream.cpp new file mode 100644 index 00000000..cd73bcd5 --- /dev/null +++ b/src/boost/libs/log/test/run/util_formatting_ostream.cpp @@ -0,0 +1,503 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_formatting_ostream.cpp + * \author Andrey Semashev + * \date 26.05.2013 + * + * \brief This header contains tests for the formatting output stream wrapper. + */ + +#define BOOST_TEST_MODULE util_formatting_ostream + +#include <locale> +#include <string> +#include <iomanip> +#include <sstream> +#include <iostream> +#include <algorithm> +#include <boost/config.hpp> +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +#include <string_view> +#endif +#include <boost/test/unit_test.hpp> +#include <boost/utility/string_view.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include "char_definitions.hpp" + +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +#define BOOST_UTF8_DECL +#define BOOST_UTF8_BEGIN_NAMESPACE namespace { +#define BOOST_UTF8_END_NAMESPACE } + +#include <boost/detail/utf8_codecvt_facet.hpp> +#include <boost/detail/utf8_codecvt_facet.ipp> + +#endif // defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +namespace logging = boost::log; + +namespace { + +struct unreferencable_data +{ + unsigned int m : 2; + unsigned int n : 6; + + enum my_enum + { + one = 1, + two = 2 + }; + + // The following static constants don't have definitions, so they can only be used in constant expressions. + // Trying to bind a reference to these members will result in linking errors. + static const int x = 7; + static const my_enum y = one; + + unreferencable_data() + { + m = 1; + n = 5; + } +}; + +template< typename CharT > +struct test_impl +{ + typedef CharT char_type; + typedef test_data< char_type > strings; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + + template< typename StringT > + static void width_formatting() + { + // Check that widening works + { + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + strm_fmt << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); + } + + // Check that the string is not truncated + { + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + strm_fmt << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); + } + } + + template< typename StringT > + static void fill_formatting() + { + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + strm_fmt << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); + } + + template< typename StringT > + static void alignment() + { + // Left alignment + { + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + strm_fmt << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); + } + + // Right alignment + { + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + strm_fmt << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC(); + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); + } + } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template< typename StringT > + static void rvalue_stream() + { + string_type str_fmt; + formatting_ostream_type(str_fmt) << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC() << std::flush; + + ostream_type strm_correct; + strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC(); + + BOOST_CHECK(equal_strings(str_fmt, strm_correct.str())); + } +#endif + + static void output_unreferencable_data() + { + unreferencable_data data; + { + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + strm_fmt << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << static_cast< unsigned int >(data.m) << static_cast< char_type >(' ') << static_cast< unsigned int >(data.n) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::x) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::y); + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); + } +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + { + string_type str_fmt; + formatting_ostream_type(str_fmt) << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y << std::flush; + + ostream_type strm_correct; + strm_correct << static_cast< unsigned int >(data.m) << static_cast< char_type >(' ') << static_cast< unsigned int >(data.n) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::x) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::y); + + BOOST_CHECK(equal_strings(str_fmt, strm_correct.str())); + } +#endif + } +}; + +} // namespace + +// Test support for width formatting +BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >(); + test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >(); +#endif +} + +// Test support for filler character setup +BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >(); + test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >(); +#endif +} + +// Test support for text alignment +BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE alignment< const CharT* >(); + test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >(); +#endif +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +// Test support for rvalue stream objects +BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >(); + test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >(); + test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >(); +#endif +} +#endif + +// Test output of data to which a reference cannot be bound +BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data, CharT, char_types) +{ + typedef test_impl< CharT > test; + test::output_unreferencable_data(); +} + +namespace my_namespace { + +class A {}; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, A const&) +{ + strm << "A"; + return strm; +} + +class B {}; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B&) +{ + strm << "B"; + return strm; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*) +{ + strm << "B*"; + return strm; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*) +{ + strm << "const B*"; + return strm; +} + +class C {}; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&) +{ + strm << "C"; + return strm; +} + +enum E { eee }; +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, E) +{ + strm << "E"; + return strm; +} + +} // namespace my_namespace + +// Test operator forwarding +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + const my_namespace::A a = my_namespace::A(); // const lvalue + my_namespace::B b; // lvalue + strm_fmt << a << b << my_namespace::C(); // rvalue + strm_fmt << my_namespace::eee; + strm_fmt << &b << (my_namespace::B const*)&b; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +namespace my_namespace2 { + +class A {}; +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, A const&) +{ + strm << "A"; + return strm; +} + +class B {}; +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, B&) +{ + strm << "B"; + return strm; +} + +class C {}; +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, C const&) +{ + strm << "C"; + return strm; +} + +class D {}; +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + D&& +#else + D const& +#endif + ) +{ + strm << "D"; + return strm; +} + +enum E { eee }; +template< typename CharT, typename TraitsT, typename AllocatorT > +inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, E) +{ + strm << "E"; + return strm; +} + +} // namespace my_namespace2 + +// Test operator overriding +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_string< char_type > string_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + const my_namespace2::A a = my_namespace2::A(); // const lvalue + my_namespace2::B b; // lvalue + strm_fmt << a << b << my_namespace2::C() << my_namespace2::D(); // rvalue + strm_fmt << my_namespace2::eee; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "ABCDE"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) + +namespace { + +const char narrow_chars[] = "\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, \xd0\xbc\xd0\xb8\xd1\x80!"; +const wchar_t wide_chars[] = { 0x041f, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, L',', L' ', 0x043c, 0x0438, 0x0440, L'!', 0 }; + +template< typename StringT > +void test_narrowing_code_conversion() +{ + std::locale loc(std::locale::classic(), new utf8_codecvt_facet()); + + // Test rvalues + { + std::string str_fmt; + logging::formatting_ostream strm_fmt(str_fmt); + strm_fmt.imbue(loc); + strm_fmt << (StringT)wide_chars; + strm_fmt.flush(); + + BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars))); + } + // Test lvalues + { + std::string str_fmt; + logging::formatting_ostream strm_fmt(str_fmt); + strm_fmt.imbue(loc); + StringT wstr = StringT(wide_chars); + strm_fmt << wstr; + strm_fmt.flush(); + + BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars))); + } + // Test const lvalues + { + std::string str_fmt; + logging::formatting_ostream strm_fmt(str_fmt); + strm_fmt.imbue(loc); + const StringT wstr = StringT(wide_chars); + strm_fmt << wstr; + strm_fmt.flush(); + + BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars))); + } +} + +template< typename StringT > +void test_widening_code_conversion() +{ + std::locale loc(std::locale::classic(), new utf8_codecvt_facet()); + + // Test rvalues + { + std::wstring str_fmt; + logging::wformatting_ostream strm_fmt(str_fmt); + strm_fmt.imbue(loc); + strm_fmt << (StringT)narrow_chars; + strm_fmt.flush(); + + BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars))); + } + // Test lvalues + { + std::wstring str_fmt; + logging::wformatting_ostream strm_fmt(str_fmt); + strm_fmt.imbue(loc); + StringT str = StringT(narrow_chars); + strm_fmt << str; + strm_fmt.flush(); + + BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars))); + } + // Test const lvalues + { + std::wstring str_fmt; + logging::wformatting_ostream strm_fmt(str_fmt); + strm_fmt.imbue(loc); + const StringT str = StringT(narrow_chars); + strm_fmt << str; + strm_fmt.flush(); + + BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars))); + } +} + +} // namespace + +// Test character code conversion +BOOST_AUTO_TEST_CASE(character_code_conversion) +{ + test_narrowing_code_conversion< const wchar_t* >(); + test_widening_code_conversion< const char* >(); + test_narrowing_code_conversion< std::wstring >(); + test_widening_code_conversion< std::string >(); + test_narrowing_code_conversion< boost::wstring_view >(); + test_widening_code_conversion< boost::string_view >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test_narrowing_code_conversion< std::wstring_view >(); + test_widening_code_conversion< std::string_view >(); +#endif +} + +#endif diff --git a/src/boost/libs/log/test/run/util_ipc_object_name.cpp b/src/boost/libs/log/test/run/util_ipc_object_name.cpp new file mode 100644 index 00000000..11e63e85 --- /dev/null +++ b/src/boost/libs/log/test/run/util_ipc_object_name.cpp @@ -0,0 +1,177 @@ +/* + * Copyright Andrey Semashev 2016. + * 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_ipc_object_name.cpp + * \author Andrey Semashev + * \date 07.03.2016 + * + * \brief The test verifies that \c ipc::object_name works. + */ + +#if !defined(BOOST_LOG_WITHOUT_IPC) + +#define BOOST_TEST_MODULE util_ipc_object_name + +#include <boost/log/utility/ipc/object_name.hpp> +#include <boost/test/unit_test.hpp> +#include <string> +#include <iostream> +#include <boost/move/utility_core.hpp> +#include "char_definitions.hpp" + +const char test_object_name1[] = "boost_log_test_object_name1"; +const char test_object_name2[] = "boost_log_test_object_name2"; + +BOOST_AUTO_TEST_CASE(basic_functionality) +{ + // Default constructor. + { + boost::log::ipc::object_name name; + BOOST_CHECK(name.empty()); + BOOST_CHECK(equal_strings(name.c_str(), "")); + } + + // Initializing constructor + { + boost::log::ipc::object_name name(boost::log::ipc::object_name::global, test_object_name1); + BOOST_CHECK(!name.empty()); + } + + // Copy constructor + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2 = name1; + BOOST_CHECK_EQUAL(name1, name2); + } + + // Move constructor + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + std::string name_str = name1.c_str(); + boost::log::ipc::object_name name2 = boost::move(name1); + BOOST_CHECK(equal_strings(name2.c_str(), name_str.c_str())); + } + + // Copy assignment + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2; + name2 = name1; + BOOST_CHECK_EQUAL(name1, name2); + } + + // Move assignment + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + std::string name_str = name1.c_str(); + boost::log::ipc::object_name name2; + name2 = boost::move(name1); + BOOST_CHECK(equal_strings(name2.c_str(), name_str.c_str())); + } + + // Output + { + std::cout << boost::log::ipc::object_name(boost::log::ipc::object_name::global, test_object_name1) << std::endl; + std::cout << boost::log::ipc::object_name(boost::log::ipc::object_name::user, test_object_name1) << std::endl; + std::cout << boost::log::ipc::object_name(boost::log::ipc::object_name::session, test_object_name1) << std::endl; + std::cout << boost::log::ipc::object_name(boost::log::ipc::object_name::process_group, test_object_name1) << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(from_native) +{ + boost::log::ipc::object_name name = boost::log::ipc::object_name::from_native(test_object_name1); + BOOST_CHECK(equal_strings(name.c_str(), test_object_name1)); +} + +BOOST_AUTO_TEST_CASE(name_equivalence) +{ + // Test that the same names are equal + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::global, test_object_name1); + BOOST_CHECK_EQUAL(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::user, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::user, test_object_name1); + BOOST_CHECK_EQUAL(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::session, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::session, test_object_name1); + BOOST_CHECK_EQUAL(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::process_group, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::process_group, test_object_name1); + BOOST_CHECK_EQUAL(name1, name2); + } + + // Test that different names don't clash + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::global, test_object_name2); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::user, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::user, test_object_name2); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::session, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::session, test_object_name2); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::process_group, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::process_group, test_object_name2); + BOOST_CHECK_NE(name1, name2); + } + + // Test that same named in different scopes don't clash + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::user, test_object_name1); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::session, test_object_name1); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::global, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::process_group, test_object_name1); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::user, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::session, test_object_name1); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::user, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::process_group, test_object_name1); + BOOST_CHECK_NE(name1, name2); + } + { + boost::log::ipc::object_name name1(boost::log::ipc::object_name::session, test_object_name1); + boost::log::ipc::object_name name2(boost::log::ipc::object_name::process_group, test_object_name1); + BOOST_CHECK_NE(name1, name2); + } +} + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/src/boost/libs/log/test/run/util_ipc_reliable_mq.cpp b/src/boost/libs/log/test/run/util_ipc_reliable_mq.cpp new file mode 100644 index 00000000..38865729 --- /dev/null +++ b/src/boost/libs/log/test/run/util_ipc_reliable_mq.cpp @@ -0,0 +1,474 @@ +/* + * Copyright Lingxi Li 2015. + * Copyright Andrey Semashev 2016. + * 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_ipc_reliable_mq.cpp + * \author Lingxi Li + * \author Andrey Semashev + * \date 19.10.2015 + * + * \brief The test verifies that \c ipc::reliable_message_queue works. + */ + +#if !defined(BOOST_LOG_WITHOUT_IPC) + +#define BOOST_TEST_MODULE util_ipc_reliable_mq + +#include <boost/log/utility/ipc/reliable_message_queue.hpp> +#include <boost/log/utility/ipc/object_name.hpp> +#include <boost/log/utility/permissions.hpp> +#include <boost/log/utility/open_mode.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/test/unit_test.hpp> +#include <cstddef> +#include <cstring> +#include <string> +#include <vector> +#include <iostream> +#include <stdexcept> +#include <boost/move/utility_core.hpp> +#if !defined(BOOST_LOG_NO_THREADS) +#include <algorithm> +#include <boost/ref.hpp> +#include <boost/atomic/fences.hpp> +#include <boost/thread/thread.hpp> +#include <boost/chrono/duration.hpp> +#endif +#include "char_definitions.hpp" + +typedef boost::log::ipc::reliable_message_queue queue_t; +typedef queue_t::size_type size_type; + +const boost::log::ipc::object_name ipc_queue_name(boost::log::ipc::object_name::session, "boost_log_test_ipc_reliable_mq"); +const unsigned int capacity = 512; +const size_type block_size = 1024; +const char message1[] = "Hello, world!"; +const char message2[] = "Hello, the brand new world!"; + +BOOST_AUTO_TEST_CASE(basic_functionality) +{ + // Default constructor. + { + queue_t queue; + BOOST_CHECK(!queue.is_open()); + } + + // Do a remove in case if a previous test failed + queue_t::remove(ipc_queue_name); + + // Opening a non-existing queue + try + { + queue_t queue(boost::log::open_mode::open_only, ipc_queue_name); + BOOST_FAIL("Non-existing queue open succeeded, although it shouldn't have"); + } + catch (std::exception&) + { + BOOST_TEST_PASSPOINT(); + } + + // Create constructor and destructor. + { + queue_t queue(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + BOOST_CHECK(equal_strings(queue.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK(queue.is_open()); + BOOST_CHECK_EQUAL(queue.capacity(), capacity); + BOOST_CHECK_EQUAL(queue.block_size(), block_size); + } + + // Creating a duplicate queue + try + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + queue_t queue_b(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + BOOST_FAIL("Creating a duplicate queue succeeded, although it shouldn't have"); + } + catch (std::exception&) + { + BOOST_TEST_PASSPOINT(); + } + + // Opening an existing queue + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + BOOST_CHECK(queue_a.is_open()); + + queue_t queue_b(boost::log::open_mode::open_or_create, ipc_queue_name, capacity * 2u, block_size * 2u); // queue geometry differs from the existing queue + BOOST_CHECK(queue_b.is_open()); + BOOST_CHECK(equal_strings(queue_b.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK_EQUAL(queue_b.capacity(), capacity); + BOOST_CHECK_EQUAL(queue_b.block_size(), block_size); + + queue_t queue_c(boost::log::open_mode::open_only, ipc_queue_name); + BOOST_CHECK(queue_c.is_open()); + BOOST_CHECK(equal_strings(queue_c.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK_EQUAL(queue_c.capacity(), capacity); + BOOST_CHECK_EQUAL(queue_c.block_size(), block_size); + } + // Closing a queue + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + BOOST_CHECK(queue_a.is_open()); + queue_a.close(); + BOOST_CHECK(!queue_a.is_open()); + // Duplicate close() + queue_a.close(); + BOOST_CHECK(!queue_a.is_open()); + } + // Move constructor. + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + queue_t queue_b(boost::move(queue_a)); + BOOST_CHECK(!queue_a.is_open()); + BOOST_CHECK(equal_strings(queue_b.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK(queue_b.is_open()); + BOOST_CHECK_EQUAL(queue_b.capacity(), capacity); + BOOST_CHECK_EQUAL(queue_b.block_size(), block_size); + } + // Move assignment operator. + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + queue_t queue_b; + queue_b = boost::move(queue_a); + BOOST_CHECK(!queue_a.is_open()); + BOOST_CHECK(equal_strings(queue_b.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK(queue_b.is_open()); + BOOST_CHECK_EQUAL(queue_b.capacity(), capacity); + BOOST_CHECK_EQUAL(queue_b.block_size(), block_size); + } + // Member and non-member swaps. + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, capacity, block_size); + queue_a.swap(queue_a); + BOOST_CHECK(queue_a.is_open()); + BOOST_CHECK(equal_strings(queue_a.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK_EQUAL(queue_a.capacity(), capacity); + BOOST_CHECK_EQUAL(queue_a.block_size(), block_size); + + queue_t queue_b; + swap(queue_a, queue_b); + BOOST_CHECK(!queue_a.is_open()); + BOOST_CHECK(queue_b.is_open()); + BOOST_CHECK(equal_strings(queue_b.name().c_str(), ipc_queue_name.c_str())); + BOOST_CHECK_EQUAL(queue_b.capacity(), capacity); + BOOST_CHECK_EQUAL(queue_b.block_size(), block_size); + } +} + +BOOST_AUTO_TEST_CASE(message_passing) +{ + // try_send() and try_receive() + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, 1u, block_size); + queue_t queue_b(boost::log::open_mode::open_only, ipc_queue_name); + BOOST_CHECK(queue_a.try_send(message1, sizeof(message1) - 1u)); + BOOST_CHECK(!queue_a.try_send(message2, sizeof(message2) - 1u)); + char buffer[block_size] = {}; + size_type message_size = 0u; + BOOST_CHECK(queue_b.try_receive(buffer, sizeof(buffer), message_size)); + BOOST_CHECK_EQUAL(message_size, sizeof(message1) - 1u); + BOOST_CHECK(std::memcmp(buffer, message1, message_size) == 0); + BOOST_CHECK(!queue_b.try_receive(buffer, sizeof(buffer), message_size)); + + BOOST_CHECK(queue_a.try_send(message2, sizeof(message2) - 1u)); + std::string msg; + BOOST_CHECK(queue_b.try_receive(msg)); + BOOST_CHECK_EQUAL(msg.size(), sizeof(message2) - 1u); + BOOST_CHECK_EQUAL(msg, message2); + + BOOST_CHECK(queue_a.try_send(message2, sizeof(message2) - 1u)); + std::vector< unsigned char > buf; + BOOST_CHECK(queue_b.try_receive(buf)); + BOOST_CHECK_EQUAL(buf.size(), sizeof(message2) - 1u); + BOOST_CHECK(std::memcmp(&buf[0], message2, buf.size()) == 0); + } + + // send() and receive() without blocking + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, 1u, block_size); + queue_t queue_b(boost::log::open_mode::open_only, ipc_queue_name); + BOOST_CHECK(queue_a.send(message1, sizeof(message1) - 1u) == queue_t::succeeded); + char buffer[block_size] = {}; + size_type message_size = 0u; + BOOST_CHECK(queue_b.receive(buffer, sizeof(buffer), message_size) == queue_t::succeeded); + BOOST_CHECK_EQUAL(message_size, sizeof(message1) - 1u); + BOOST_CHECK(std::memcmp(buffer, message1, message_size) == 0); + + BOOST_CHECK(queue_a.send(message2, sizeof(message2) - 1u) == queue_t::succeeded); + std::string msg; + BOOST_CHECK(queue_b.receive(msg) == queue_t::succeeded); + BOOST_CHECK_EQUAL(msg.size(), sizeof(message2) - 1u); + BOOST_CHECK_EQUAL(msg, message2); + + BOOST_CHECK(queue_a.send(message2, sizeof(message2) - 1u) == queue_t::succeeded); + std::vector< unsigned char > buf; + BOOST_CHECK(queue_b.receive(buf) == queue_t::succeeded); + BOOST_CHECK_EQUAL(buf.size(), sizeof(message2) - 1u); + BOOST_CHECK(std::memcmp(&buf[0], message2, buf.size()) == 0); + } + + // send() with an error code on overflow + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, 1u, block_size, queue_t::fail_on_overflow); + BOOST_TEST_PASSPOINT(); + BOOST_CHECK(queue_a.send(message1, sizeof(message1) - 1u) == queue_t::succeeded); + BOOST_TEST_PASSPOINT(); + + queue_t::operation_result res = queue_a.send(message1, sizeof(message1) - 1u); + BOOST_CHECK_EQUAL(res, queue_t::no_space); + } + + // send() with an exception on overflow + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, 1u, block_size, queue_t::throw_on_overflow); + BOOST_TEST_PASSPOINT(); + BOOST_CHECK(queue_a.send(message1, sizeof(message1) - 1u) == queue_t::succeeded); + BOOST_TEST_PASSPOINT(); + try + { + queue_a.send(message1, sizeof(message1) - 1u); + BOOST_FAIL("Owerflowing the queue succeeded, although it shouldn't have"); + } + catch (boost::log::capacity_limit_reached&) + { + BOOST_TEST_PASSPOINT(); + } + } + + // send() and receive() for messages larger than block_size. The message size and queue capacity below are such + // that the last enqueued message is expected to be split in the queue storage. + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, 5u, block_size); + queue_t queue_b(boost::log::open_mode::open_only, ipc_queue_name); + + const size_type message_size = block_size * 3u / 2u; + std::vector< unsigned char > send_data; + send_data.resize(message_size); + for (unsigned int i = 0; i < message_size; ++i) + send_data[i] = static_cast< unsigned char >(i & 0xFF); + + BOOST_CHECK(queue_a.send(&send_data[0], static_cast< size_type >(send_data.size())) == queue_t::succeeded); + + for (unsigned int i = 0; i < 3; ++i) + { + BOOST_CHECK(queue_a.send(&send_data[0], static_cast< size_type >(send_data.size())) == queue_t::succeeded); + + std::vector< unsigned char > receive_data; + BOOST_CHECK(queue_b.receive(receive_data) == queue_t::succeeded); + BOOST_CHECK_EQUAL_COLLECTIONS(send_data.begin(), send_data.end(), receive_data.begin(), receive_data.end()); + } + + std::vector< unsigned char > receive_data; + BOOST_CHECK(queue_b.receive(receive_data) == queue_t::succeeded); + BOOST_CHECK_EQUAL_COLLECTIONS(send_data.begin(), send_data.end(), receive_data.begin(), receive_data.end()); + } + + // clear() + { + queue_t queue_a(boost::log::open_mode::create_only, ipc_queue_name, 1u, block_size); + queue_t queue_b(boost::log::open_mode::open_only, ipc_queue_name); + BOOST_CHECK(queue_a.try_send(message1, sizeof(message1) - 1u)); + BOOST_CHECK(!queue_a.try_send(message2, sizeof(message2) - 1u)); + + queue_a.clear(); + + BOOST_CHECK(queue_a.try_send(message2, sizeof(message2) - 1u)); + char buffer[block_size] = {}; + size_type message_size = 0u; + BOOST_CHECK(queue_b.try_receive(buffer, sizeof(buffer), message_size)); + BOOST_CHECK_EQUAL(message_size, sizeof(message2) - 1u); + BOOST_CHECK(std::memcmp(buffer, message2, message_size) == 0); + } +} + +#if !defined(BOOST_LOG_NO_THREADS) + +namespace { + +const unsigned int message_count = 100000; + +void multithreaded_message_passing_feeding_thread(const char* message, unsigned int& failure_count) +{ + const size_type len = static_cast< size_type >(std::strlen(message)); + queue_t queue(boost::log::open_mode::open_or_create, ipc_queue_name, capacity, block_size); + for (unsigned int i = 0; i < message_count; ++i) + { + failure_count += queue.send(message, len) != queue_t::succeeded; + } + + boost::atomic_thread_fence(boost::memory_order_release); +} + +} // namespace + +BOOST_AUTO_TEST_CASE(multithreaded_message_passing) +{ + unsigned int failure_count1 = 0, failure_count2 = 0, failure_count3 = 0; + boost::atomic_thread_fence(boost::memory_order_release); + + boost::thread thread1(&multithreaded_message_passing_feeding_thread, "Thread 1", boost::ref(failure_count1)); + boost::thread thread2(&multithreaded_message_passing_feeding_thread, "Thread 2", boost::ref(failure_count2)); + boost::thread thread3(&multithreaded_message_passing_feeding_thread, "Thread 3", boost::ref(failure_count3)); + + BOOST_TEST_PASSPOINT(); + + queue_t queue(boost::log::open_mode::open_or_create, ipc_queue_name, capacity, block_size); + unsigned int receive_failures = 0, receive_corruptions = 0; + unsigned int message_count1 = 0, message_count2 = 0, message_count3 = 0; + std::string msg; + + BOOST_TEST_PASSPOINT(); + + for (unsigned int i = 0; i < message_count * 3u; ++i) + { + msg.clear(); + if (queue.receive(msg) == queue_t::succeeded) + { + if (msg == "Thread 1") + ++message_count1; + else if (msg == "Thread 2") + ++message_count2; + else if (msg == "Thread 3") + ++message_count3; + else + ++receive_corruptions; + } + else + ++receive_failures; + } + + BOOST_TEST_PASSPOINT(); + thread1.join(); + + BOOST_TEST_PASSPOINT(); + thread2.join(); + + BOOST_TEST_PASSPOINT(); + thread3.join(); + + boost::atomic_thread_fence(boost::memory_order_acquire); + + BOOST_CHECK_EQUAL(failure_count1, 0u); + BOOST_CHECK_EQUAL(message_count1, message_count); + BOOST_CHECK_EQUAL(failure_count2, 0u); + BOOST_CHECK_EQUAL(message_count2, message_count); + BOOST_CHECK_EQUAL(failure_count3, 0u); + BOOST_CHECK_EQUAL(message_count3, message_count); + BOOST_CHECK_EQUAL(receive_failures, 0u); + BOOST_CHECK_EQUAL(receive_corruptions, 0u); +} + +namespace { + +void stop_reset_feeding_thread(queue_t& queue, queue_t::operation_result* results, unsigned int count) +{ + for (unsigned int i = 0; i < count; ++i) + { + results[i] = queue.send(message1, sizeof(message1) - 1u); + if (results[i] != queue_t::succeeded) + break; + } + + boost::atomic_thread_fence(boost::memory_order_release); +} + +void stop_reset_reading_thread(queue_t& queue, queue_t::operation_result* results, unsigned int count) +{ + std::string msg; + for (unsigned int i = 0; i < count; ++i) + { + msg.clear(); + results[i] = queue.receive(msg); + if (results[i] != queue_t::succeeded) + break; + } + + boost::atomic_thread_fence(boost::memory_order_release); +} + +} // namespace + +BOOST_AUTO_TEST_CASE(stop_reset_local) +{ + queue_t feeder_queue(boost::log::open_mode::open_or_create, ipc_queue_name, 1u, block_size); + queue_t::operation_result feeder_results[3]; + queue_t reader_queue(boost::log::open_mode::open_only, ipc_queue_name); + queue_t::operation_result reader_results[3]; + + std::fill_n(feeder_results, sizeof(feeder_results) / sizeof(*feeder_results), queue_t::succeeded); + std::fill_n(reader_results, sizeof(reader_results) / sizeof(*reader_results), queue_t::succeeded); + boost::atomic_thread_fence(boost::memory_order_release); + + BOOST_TEST_PASSPOINT(); + + // Case 1: Let the feeder block and then we unblock it with stop_local() + boost::thread feeder_thread(&stop_reset_feeding_thread, boost::ref(feeder_queue), feeder_results, 3); + boost::thread reader_thread(&stop_reset_reading_thread, boost::ref(reader_queue), reader_results, 1); + + BOOST_TEST_PASSPOINT(); + + reader_thread.join(); + BOOST_TEST_PASSPOINT(); + boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); + + BOOST_TEST_PASSPOINT(); + + feeder_queue.stop_local(); + BOOST_TEST_PASSPOINT(); + feeder_thread.join(); + + boost::atomic_thread_fence(boost::memory_order_acquire); + + BOOST_CHECK_EQUAL(feeder_results[0], queue_t::succeeded); + BOOST_CHECK_EQUAL(feeder_results[1], queue_t::succeeded); + BOOST_CHECK_EQUAL(feeder_results[2], queue_t::aborted); + BOOST_CHECK_EQUAL(reader_results[0], queue_t::succeeded); + + // Reset the aborted queue + feeder_queue.reset_local(); + feeder_queue.clear(); + + std::fill_n(feeder_results, sizeof(feeder_results) / sizeof(*feeder_results), queue_t::succeeded); + std::fill_n(reader_results, sizeof(reader_results) / sizeof(*reader_results), queue_t::succeeded); + boost::atomic_thread_fence(boost::memory_order_release); + + BOOST_TEST_PASSPOINT(); + + // Case 2: Let the reader block and then we unblock it with stop_local() + boost::thread(&stop_reset_feeding_thread, boost::ref(feeder_queue), feeder_results, 1).swap(feeder_thread); + boost::thread(&stop_reset_reading_thread, boost::ref(reader_queue), reader_results, 2).swap(reader_thread); + + BOOST_TEST_PASSPOINT(); + + feeder_thread.join(); + BOOST_TEST_PASSPOINT(); + boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); + + BOOST_TEST_PASSPOINT(); + + reader_queue.stop_local(); + BOOST_TEST_PASSPOINT(); + reader_thread.join(); + + boost::atomic_thread_fence(boost::memory_order_acquire); + + BOOST_CHECK_EQUAL(feeder_results[0], queue_t::succeeded); + BOOST_CHECK_EQUAL(feeder_results[1], queue_t::succeeded); + BOOST_CHECK_EQUAL(reader_results[0], queue_t::succeeded); + BOOST_CHECK_EQUAL(reader_results[1], queue_t::aborted); +} + +#endif // !defined(BOOST_LOG_NO_THREADS) + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/src/boost/libs/log/test/run/util_manip_add_value.cpp b/src/boost/libs/log/test/run/util_manip_add_value.cpp new file mode 100644 index 00000000..9b2227e5 --- /dev/null +++ b/src/boost/libs/log/test/run/util_manip_add_value.cpp @@ -0,0 +1,139 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_add_value.cpp + * \author Andrey Semashev + * \date 07.11.2013 + * + * \brief This header contains tests for the \c add_value manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_add_value + +#include <iomanip> +#include <iostream> +#include <boost/move/core.hpp> +#include <boost/io/ios_state.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/log/core.hpp> +#include <boost/log/sources/record_ostream.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/value_extraction.hpp> +#include <boost/log/expressions/keyword.hpp> +#include <boost/log/utility/manipulators/add_value.hpp> +#include "make_record.hpp" + +namespace logging = boost::log; + +struct my_type +{ + BOOST_COPYABLE_AND_MOVABLE(my_type) + +public: + unsigned int value; + + explicit my_type(unsigned int n = 0) : value(n) {} + my_type(my_type const& that) : value(that.value) {} + my_type(BOOST_RV_REF(my_type) that) : value(that.value) { that.value = 0xbaadbaad; } + ~my_type() { value = 0xdeaddead; } + + my_type& operator= (BOOST_COPY_ASSIGN_REF(my_type) that) { value = that.value; return *this; } + my_type& operator= (BOOST_RV_REF(my_type) that) { value = that.value; that.value = 0xbaadbaad; return *this; } +}; + +inline bool operator== (my_type const& left, my_type const& right) +{ + return left.value == right.value; +} + +inline bool operator!= (my_type const& left, my_type const& right) +{ + return left.value != right.value; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_type const& val) +{ + if (strm.good()) + { + boost::io::ios_flags_saver flags(strm); + boost::io::basic_ios_fill_saver< CharT, TraitsT > fill(strm); + strm << std::hex << std::internal << std::setfill(static_cast< CharT >('0')) << std::setw(10) << val.value; + } + return strm; +} + +struct my_pod_type +{ + unsigned int value; +}; + +inline bool operator== (my_pod_type const& left, my_pod_type const& right) +{ + return left.value == right.value; +} + +inline bool operator!= (my_pod_type const& left, my_pod_type const& right) +{ + return left.value != right.value; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_pod_type const& val) +{ + if (strm.good()) + { + boost::io::ios_flags_saver flags(strm); + boost::io::basic_ios_fill_saver< CharT, TraitsT > fill(strm); + strm << std::hex << std::internal << std::setfill(static_cast< CharT >('0')) << std::setw(10) << val.value; + } + return strm; +} + +BOOST_AUTO_TEST_CASE(manual_add_attr) +{ + logging::record rec = make_record(logging::attribute_set()); + BOOST_REQUIRE(!!rec); + logging::record_ostream strm(rec); + + my_type val(0xaaaaaaaa); + const my_type const_val(0xbbbbbbbb); + strm << logging::add_value("MyAttr1", val) << logging::add_value("MyAttr2", const_val) << logging::add_value("MyAttr3", my_type(0xcccccccc)); + + // Test for MSVC bug: if the value is a scalar type, it saves a dangling reference to the add_value_manip, + // which results in garbage in the attribute value + strm << logging::add_value("MyAttr4", 100u); + strm << logging::add_value("MyAttr5", my_pod_type()); + + strm.detach_from_record(); + + BOOST_CHECK_EQUAL(rec["MyAttr1"].extract< my_type >(), val); + BOOST_CHECK_EQUAL(rec["MyAttr2"].extract< my_type >(), const_val); + BOOST_CHECK_EQUAL(rec["MyAttr3"].extract< my_type >(), my_type(0xcccccccc)); + BOOST_CHECK_EQUAL(rec["MyAttr4"].extract< unsigned int >(), 100u); + BOOST_CHECK_EQUAL(rec["MyAttr5"].extract< my_pod_type >(), my_pod_type()); +} + +BOOST_LOG_ATTRIBUTE_KEYWORD(a_my1, "MyAttr1", my_type) +BOOST_LOG_ATTRIBUTE_KEYWORD(a_my2, "MyAttr2", my_type) +BOOST_LOG_ATTRIBUTE_KEYWORD(a_my3, "MyAttr3", my_type) + +BOOST_AUTO_TEST_CASE(keyword_add_attr) +{ + logging::record rec = make_record(logging::attribute_set()); + BOOST_REQUIRE(!!rec); + logging::record_ostream strm(rec); + + my_type val(0xaaaaaaaa); + const my_type const_val(0xbbbbbbbb); + strm << logging::add_value(a_my1, val) << logging::add_value(a_my2, const_val) << logging::add_value(a_my3, my_type(0xcccccccc)); + strm.detach_from_record(); + + BOOST_CHECK_EQUAL(rec[a_my1], val); + BOOST_CHECK_EQUAL(rec[a_my2], const_val); + BOOST_CHECK_EQUAL(rec[a_my3], my_type(0xcccccccc)); +} diff --git a/src/boost/libs/log/test/run/util_manip_auto_newline.cpp b/src/boost/libs/log/test/run/util_manip_auto_newline.cpp new file mode 100644 index 00000000..91f1d07b --- /dev/null +++ b/src/boost/libs/log/test/run/util_manip_auto_newline.cpp @@ -0,0 +1,83 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file util_manip_auto_newline.cpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * \brief This header contains tests for the auto_newline manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_auto_newline + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/manipulators/auto_newline.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; + +// Test appending a newline to a non-empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_non_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + strm_fmt << "Hello" << logging::auto_newline; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test appending a newline to an empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + strm_fmt << logging::auto_newline; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test not appending a newline to a non-empty string which already ends with a newline +BOOST_AUTO_TEST_CASE_TEMPLATE(not_append_if_ends_with_a_newline, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + strm_fmt << "Hello\n" << logging::auto_newline; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} diff --git a/src/boost/libs/log/test/run/util_manip_dump.cpp b/src/boost/libs/log/test/run/util_manip_dump.cpp new file mode 100644 index 00000000..731116cd --- /dev/null +++ b/src/boost/libs/log/test/run/util_manip_dump.cpp @@ -0,0 +1,239 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 dump manipulator. + */ + +#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_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_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_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_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 SIMD tail handling +BOOST_AUTO_TEST_CASE_TEMPLATE(unbounded_binary_tail_dump, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + std::vector< unsigned char > data; + ostream_type strm_correct; + // 1023 makes it very unlikely for the buffer to end at 16 or 32 byte boundary, which makes the dump algorithm to process the tail in a special way + for (unsigned int i = 0; i < 1023; ++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_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_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_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())); +} + diff --git a/src/boost/libs/log/test/run/util_manip_to_log.cpp b/src/boost/libs/log/test/run/util_manip_to_log.cpp new file mode 100644 index 00000000..3937d540 --- /dev/null +++ b/src/boost/libs/log/test/run/util_manip_to_log.cpp @@ -0,0 +1,134 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_to_log.cpp + * \author Andrey Semashev + * \date 05.07.2015 + * + * \brief This header contains tests for the \c to_log stream manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_to_log + +#include <string> +#include <sstream> +#include <algorithm> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/utility/manipulators/to_log.hpp> +#include "char_definitions.hpp" + +namespace logging = boost::log; + +namespace tag { + +struct a_my_class; + +} // namespace tag + +namespace { + +struct my_class +{ + int m_Data; + + explicit my_class(int data) : m_Data(data) {} +}; + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& +operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_class const& obj) +{ + strm << "my_class: [data: " << obj.m_Data << "]"; + return strm; +} + +template< typename StreamT > +inline StreamT& +operator<< (StreamT& strm, logging::to_log_manip< my_class > const& obj) +{ + strm << "to_log(my_class: [data: " << obj.get().m_Data << "])"; + return strm; +} + +template< typename StreamT > +inline StreamT& +operator<< (StreamT& strm, logging::to_log_manip< my_class, tag::a_my_class > const& obj) +{ + strm << "to_log<a_my_class>(my_class: [data: " << obj.get().m_Data << "])"; + return strm; +} + +template< typename CharT, typename StreamT > +struct tests +{ + typedef CharT char_type; + typedef StreamT stream_type; + typedef std::basic_string< char_type > string; + typedef std::basic_ostringstream< char_type > std_stream; + + //! The test verifies that the default behavior of the manipulator is equivalent to the standard operator<< + static void default_operator() + { + string str; + stream_type strm1(str); + strm1 << logging::to_log(10); + + std_stream strm2; + strm2 << 10; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + + //! The test verifies that operator overrides work + static void operator_overrides() + { + { + string str; + stream_type strm1(str); + strm1 << my_class(10); + + std_stream strm2; + strm2 << "my_class: [data: " << 10 << "]"; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str; + stream_type strm1(str); + strm1 << logging::to_log(my_class(10)); + + std_stream strm2; + strm2 << "to_log(my_class: [data: " << 10 << "])"; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + { + string str; + stream_type strm1(str); + strm1 << logging::to_log< tag::a_my_class >(my_class(10)); + + std_stream strm2; + strm2 << "to_log<a_my_class>(my_class: [data: " << 10 << "])"; + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } + } +}; + +} // namespace + + +//! The test verifies that the default behavior of the manipulator is equivalent to the standard operator<< +BOOST_AUTO_TEST_CASE_TEMPLATE(default_operator, CharT, char_types) +{ + tests< CharT, std::basic_ostringstream< CharT > >::default_operator(); + tests< CharT, logging::basic_formatting_ostream< CharT > >::default_operator(); +} + +//! The test verifies that operator overrides work +BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overrides, CharT, char_types) +{ + tests< CharT, std::basic_ostringstream< CharT > >::operator_overrides(); + tests< CharT, logging::basic_formatting_ostream< CharT > >::operator_overrides(); +} diff --git a/src/boost/libs/log/test/run/util_once_block.cpp b/src/boost/libs/log/test/run/util_once_block.cpp new file mode 100644 index 00000000..d952dad7 --- /dev/null +++ b/src/boost/libs/log/test/run/util_once_block.cpp @@ -0,0 +1,293 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_once_block.cpp + * \author Andrey Semashev + * \date 24.06.2010 + * + * \brief This header contains tests for once-blocks implementation. + * + * The test was adopted from test_once.cpp test of Boost.Thread. + */ + +#define BOOST_TEST_MODULE util_once_block + +#include <boost/log/utility/once_block.hpp> +#include <boost/test/unit_test.hpp> + +#if !defined(BOOST_LOG_NO_THREADS) + +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/locks.hpp> +#include <boost/thread/barrier.hpp> + +namespace logging = boost::log; + +enum config +{ + THREAD_COUNT = 20, + LOOP_COUNT = 100 +}; + +boost::mutex m; +typedef boost::lock_guard< boost::mutex > scoped_lock; + +logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT; +int var_to_init_once_flag = 0; + +void initialize_variable() +{ + // ensure that if multiple threads get in here, they are serialized, so we can see the effect + scoped_lock lock(m); + ++var_to_init_once_flag; +} + + +void once_block_flag_thread(boost::barrier& barrier) +{ + int my_once_value = 0; + barrier.wait(); + for (unsigned int i = 0; i < LOOP_COUNT; ++i) + { + BOOST_LOG_ONCE_BLOCK_FLAG(flag) + { + initialize_variable(); + } + + my_once_value = var_to_init_once_flag; + if (my_once_value != 1) + { + break; + } + } + scoped_lock lock(m); + BOOST_CHECK_EQUAL(my_once_value, 1); +} + +// The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works +BOOST_AUTO_TEST_CASE(once_block_flag) +{ + boost::thread_group group; + boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); + + try + { + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + { + group.create_thread(boost::bind(&once_block_flag_thread, boost::ref(barrier))); + } + group.join_all(); + } + catch (...) + { + group.interrupt_all(); + group.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(var_to_init_once_flag, 1); +} + +int var_to_init_once = 0; + +void once_block_thread(boost::barrier& barrier) +{ + int my_once_value = 0; + barrier.wait(); + for (unsigned int i = 0; i < LOOP_COUNT; ++i) + { + BOOST_LOG_ONCE_BLOCK() + { + scoped_lock lock(m); + ++var_to_init_once; + } + + my_once_value = var_to_init_once; + if (my_once_value != 1) + { + break; + } + } + + scoped_lock lock(m); + BOOST_CHECK_EQUAL(my_once_value, 1); +} + +// The test checks if the BOOST_LOG_ONCE_BLOCK macro works +BOOST_AUTO_TEST_CASE(once_block) +{ + boost::thread_group group; + boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); + + try + { + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + { + group.create_thread(boost::bind(&once_block_thread, boost::ref(barrier))); + } + group.join_all(); + } + catch(...) + { + group.interrupt_all(); + group.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(var_to_init_once, 1); +} + + +struct my_exception +{ +}; + +unsigned int pass_counter = 0; +unsigned int exception_counter = 0; + +void once_block_with_exception_thread(boost::barrier& barrier) +{ + barrier.wait(); + try + { + BOOST_LOG_ONCE_BLOCK() + { + scoped_lock lock(m); + ++pass_counter; + if (pass_counter < 3) + { + throw my_exception(); + } + } + } + catch (my_exception&) + { + scoped_lock lock(m); + ++exception_counter; + } +} + +// The test verifies that the once_block flag is not set if an exception is thrown from the once-block +BOOST_AUTO_TEST_CASE(once_block_retried_on_exception) +{ + boost::thread_group group; + boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); + + try + { + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + { + group.create_thread(boost::bind(&once_block_with_exception_thread, boost::ref(barrier))); + } + group.join_all(); + } + catch(...) + { + group.interrupt_all(); + group.join_all(); + throw; + } + + BOOST_CHECK_EQUAL(pass_counter, 3u); + BOOST_CHECK_EQUAL(exception_counter, 2u); +} + +#else // BOOST_LOG_NO_THREADS + +namespace logging = boost::log; + +enum config +{ + LOOP_COUNT = 100 +}; + +logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT; +int var_to_init_once_flag = 0; + +void initialize_variable() +{ + ++var_to_init_once_flag; +} + + +// The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works +BOOST_AUTO_TEST_CASE(once_block_flag) +{ + for (unsigned int i = 0; i < LOOP_COUNT; ++i) + { + BOOST_LOG_ONCE_BLOCK_FLAG(flag) + { + initialize_variable(); + } + + if (var_to_init_once_flag != 1) + { + break; + } + } + + BOOST_CHECK_EQUAL(var_to_init_once_flag, 1); +} + +int var_to_init_once = 0; + +// The test checks if the BOOST_LOG_ONCE_BLOCK macro works +BOOST_AUTO_TEST_CASE(once_block) +{ + for (unsigned int i = 0; i < LOOP_COUNT; ++i) + { + BOOST_LOG_ONCE_BLOCK() + { + ++var_to_init_once; + } + + if (var_to_init_once != 1) + { + break; + } + } + + BOOST_CHECK_EQUAL(var_to_init_once, 1); +} + +struct my_exception +{ +}; + +unsigned int pass_counter = 0; +unsigned int exception_counter = 0; + +// The test verifies that the once_block flag is not set if an exception is thrown from the once-block +BOOST_AUTO_TEST_CASE(once_block_retried_on_exception) +{ + for (unsigned int i = 0; i < LOOP_COUNT; ++i) + { + try + { + BOOST_LOG_ONCE_BLOCK() + { + ++pass_counter; + if (pass_counter < 3) + { + throw my_exception(); + } + } + } + catch (my_exception&) + { + ++exception_counter; + } + } + + BOOST_CHECK_EQUAL(pass_counter, 3u); + BOOST_CHECK_EQUAL(exception_counter, 2u); +} + +#endif // BOOST_LOG_NO_THREADS diff --git a/src/boost/libs/log/test/run/util_static_type_disp.cpp b/src/boost/libs/log/test/run/util_static_type_disp.cpp new file mode 100644 index 00000000..3f30ad5d --- /dev/null +++ b/src/boost/libs/log/test/run/util_static_type_disp.cpp @@ -0,0 +1,147 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_static_type_disp.cpp + * \author Andrey Semashev + * \date 09.01.2009 + * + * \brief This header contains tests for the static type dispatcher. + */ + +#define BOOST_TEST_MODULE util_static_type_disp + +#include <string> +#include <typeinfo> +#include <boost/mpl/vector.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/tools/floating_point_comparison.hpp> +#include <boost/log/utility/type_dispatch/static_type_dispatcher.hpp> + +namespace logging = boost::log; + +namespace { + + // A simple attribute value + template< typename T > + struct my_value + { + T m_Value; + + explicit my_value(T const& value) : m_Value(value) {} + + // The function passes the contained type into the dispatcher + bool dispatch(logging::type_dispatcher& dispatcher) + { + logging::type_dispatcher::callback< T > callback = dispatcher.get_callback< T >(); + if (callback) + { + callback(m_Value); + return true; + } + else + return false; + } + }; + + // The function tests general functionality of the type dispatcher + template< typename DispatcherT > + void test_general_functionality(DispatcherT& disp) + { + // These two attributes are supported by the dispatcher + my_value< std::string > val1("Hello world!"); + disp.set_expected(val1.m_Value); + BOOST_CHECK(val1.dispatch(disp)); + + my_value< double > val2(1.2); + disp.set_expected(val2.m_Value); + BOOST_CHECK(val2.dispatch(disp)); + + // This one is not + my_value< float > val3(static_cast< float >(-4.3)); + disp.set_expected(); + BOOST_CHECK(!val3.dispatch(disp)); + } + + + // Type dispatcher for the supported types + struct my_dispatcher : + public logging::static_type_dispatcher< + boost::mpl::vector< int, double, std::string > + > + { + typedef logging::static_type_dispatcher< + boost::mpl::vector< int, double, std::string > + > base_type; + + enum type_expected + { + none_expected, + int_expected, + double_expected, + string_expected + }; + + my_dispatcher() : + base_type(*this), + m_Expected(none_expected), + m_Int(0), + m_Double(0.0) + { + } + + void set_expected() + { + m_Expected = none_expected; + } + void set_expected(int value) + { + m_Expected = int_expected; + m_Int = value; + } + void set_expected(double value) + { + m_Expected = double_expected; + m_Double = value; + } + void set_expected(std::string const& value) + { + m_Expected = string_expected; + m_String = value; + } + + // Implement visitation logic for all supported types + void operator() (int const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, int_expected); + BOOST_CHECK_EQUAL(m_Int, value); + } + void operator() (double const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, double_expected); + BOOST_CHECK_CLOSE(m_Double, value, 0.001); + } + void operator() (std::string const& value) const + { + BOOST_CHECK_EQUAL(m_Expected, string_expected); + BOOST_CHECK_EQUAL(m_String, value); + } + + private: + type_expected m_Expected; + int m_Int; + double m_Double; + std::string m_String; + }; + +} // namespace + +// The test checks that general functionality works +BOOST_AUTO_TEST_CASE(type_dispatch) +{ + my_dispatcher disp; + test_general_functionality(disp); +} diff --git a/src/boost/libs/log/test/run/util_stp_filter_parser.cpp b/src/boost/libs/log/test/run/util_stp_filter_parser.cpp new file mode 100644 index 00000000..946c5f06 --- /dev/null +++ b/src/boost/libs/log/test/run/util_stp_filter_parser.cpp @@ -0,0 +1,808 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 setup_filter_parser.cpp + * \author Andrey Semashev + * \date 24.08.2013 + * + * \brief This header contains tests for the filter parser. + */ + +#define BOOST_TEST_MODULE setup_filter_parser + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/setup/filter_parser.hpp> + +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/expressions/filter.hpp> + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +typedef logging::attribute_set attr_set; +typedef logging::attribute_value_set attr_values; + +// Tests for attribute presence check +BOOST_AUTO_TEST_CASE(attr_presence) +{ + attrs::constant< int > attr1(10); + attr_set set1, set2, set3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + set1["MyAttr"] = attr1; + attr_values values2(set1, set2, set3); + values2.freeze(); + + { + logging::filter f = logging::parse_filter("%MyAttr%"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + { + logging::filter f = logging::parse_filter(" % MyAttr % "); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } +} + +// Tests for integer relation filter +BOOST_AUTO_TEST_CASE(int_relation) +{ + attrs::constant< int > attr1(10); + attrs::constant< long > attr2(20); + attrs::constant< int > attr3(-2); + attr_set set1, set2, set3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + set1["MyAttr"] = attr1; + attr_values values2(set1, set2, set3); + values2.freeze(); + + set1["MyAttr"] = attr2; + attr_values values3(set1, set2, set3); + values3.freeze(); + + set1["MyAttr"] = attr3; + attr_values values4(set1, set2, set3); + values4.freeze(); + + { + logging::filter f = logging::parse_filter("%MyAttr% = 10"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% != 10"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% < 20"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% < -7"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > 10"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(!f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > -5"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% <= 20"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(f(values4)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% >= 20"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(!f(values4)); + } +} + +// Tests for floating point relation filter +BOOST_AUTO_TEST_CASE(fp_relation) +{ + attrs::constant< float > attr1(2.5f); + attrs::constant< float > attr2(8.8f); + attrs::constant< double > attr3(-9.1); + attrs::constant< float > attr4(0.0f); + attr_set set1, set2, set3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + set1["MyAttr"] = attr1; + attr_values values2(set1, set2, set3); + values2.freeze(); + + set1["MyAttr"] = attr2; + attr_values values3(set1, set2, set3); + values3.freeze(); + + set1["MyAttr"] = attr3; + attr_values values4(set1, set2, set3); + values4.freeze(); + + set1["MyAttr"] = attr4; + attr_values values5(set1, set2, set3); + values5.freeze(); + + { + logging::filter f = logging::parse_filter("%MyAttr% = 10.3"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(!f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% != 10"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% < 5.5"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% < -7"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(!f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > 5.6"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(!f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > -5"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% <= 20"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% >= 20"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(!f(values5)); + } +} + +// Tests for string relation filter +BOOST_AUTO_TEST_CASE(string_relation) +{ + attrs::constant< std::string > attr1("hello"); + attr_set set1, set2, set3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + set1["MyStr"] = attr1; + attr_values values2(set1, set2, set3); + values2.freeze(); + + { + logging::filter f = logging::parse_filter("%MyStr% = hello"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% = \"hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + { + logging::filter f = logging::parse_filter(" % MyStr % = \"hello\" "); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% = \" hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% = \"hello \""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% = \"world\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% = \"Hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% != hello"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% != world"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% < world"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + { + logging::filter f = logging::parse_filter("%MyStr% > world"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + } + + // Check that strings that look like numbers can still be used in filters + attrs::constant< std::string > attr2("55"); + set1["MyStr"] = attr2; + attr_values values3(set1, set2, set3); + values3.freeze(); + + { + logging::filter f = logging::parse_filter("%MyStr% = \"55\""); + BOOST_CHECK(f(values3)); + } + { + logging::filter f = logging::parse_filter("%MyStr% < \"555\""); + BOOST_CHECK(f(values3)); + } + { + logging::filter f = logging::parse_filter("%MyStr% > \"44\""); + BOOST_CHECK(f(values3)); + } +} + +// Tests for multiple expression filter +BOOST_AUTO_TEST_CASE(multi_expression) +{ + attrs::constant< int > attr1(10); + attrs::constant< int > attr2(20); + attrs::constant< std::string > attr3("hello"); + attrs::constant< std::string > attr4("world"); + attr_set set1, set2, set3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + set1["MyAttr"] = attr1; + attr_values values2(set1, set2, set3); + values2.freeze(); + + set1["MyAttr"] = attr2; + attr_values values3(set1, set2, set3); + values3.freeze(); + + set1["MyStr"] = attr3; + attr_values values4(set1, set2, set3); + values4.freeze(); + + set1["MyStr"] = attr4; + attr_values values5(set1, set2, set3); + values5.freeze(); + + { + logging::filter f = logging::parse_filter("%MyAttr% = 10 & %MyStr% = \"hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(!f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > 10 & %MyStr% = \"hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(!f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > 10 and %MyStr% = \"hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(!f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% = 10 | %MyStr% = \"world\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > 10 | %MyStr% = \"world\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% = 10 or %MyStr% = \"world\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(!f(values4)); + BOOST_CHECK(f(values5)); + } + { + logging::filter f = logging::parse_filter("%MyAttr% > 0 & %MyAttr% < 20 | %MyStr% = \"hello\""); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(!f(values3)); + BOOST_CHECK(f(values4)); + BOOST_CHECK(!f(values5)); + } +} + +// Tests for negation +BOOST_AUTO_TEST_CASE(negation) +{ + attrs::constant< int > attr1(10); + attrs::constant< int > attr2(20); + attrs::constant< std::string > attr3("hello"); + attr_set set1, set2, set3; + + attr_values values1(set1, set2, set3); + values1.freeze(); + + set1["MyAttr"] = attr1; + attr_values values2(set1, set2, set3); + values2.freeze(); + + set1["MyAttr"] = attr2; + attr_values values3(set1, set2, set3); + values3.freeze(); + + set1["MyStr"] = attr3; + attr_values values4(set1, set2, set3); + values4.freeze(); + + // Test with presence filter + { + logging::filter f = logging::parse_filter("!%MyAttr%"); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter(" ! % MyAttr % "); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("not %MyAttr%"); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + } + { + logging::filter f = logging::parse_filter("!!%MyAttr%"); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(f(values2)); + } + + // Test with relations + { + logging::filter f = logging::parse_filter("!(%MyAttr% = 10)"); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + } + { + logging::filter f = logging::parse_filter("not ( %MyAttr% = 10 ) "); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + } + { + logging::filter f = logging::parse_filter("!(%MyAttr% < 20)"); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + } + + // Test with multiple subexpressions + { + logging::filter f = logging::parse_filter("!(%MyAttr% = 20 & %MyStr% = hello)"); + BOOST_CHECK(f(values1)); + BOOST_CHECK(f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(!f(values4)); + } + { + logging::filter f = logging::parse_filter("!(%MyAttr% = 10 | %MyStr% = hello)"); + BOOST_CHECK(f(values1)); + BOOST_CHECK(!f(values2)); + BOOST_CHECK(f(values3)); + BOOST_CHECK(!f(values4)); + } +} + +// Tests for begins_with relation filter +BOOST_AUTO_TEST_CASE(begins_with_relation) +{ + attrs::constant< std::string > attr1("abcdABCD"); + attr_set set1, set2, set3; + + set1["MyStr"] = attr1; + attr_values values1(set1, set2, set3); + values1.freeze(); + + { + logging::filter f = logging::parse_filter("%MyStr% begins_with \"abcd\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% begins_with \"ABCD\""); + BOOST_CHECK(!f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% begins_with \"efgh\""); + BOOST_CHECK(!f(values1)); + } +} + +// Tests for ends_with relation filter +BOOST_AUTO_TEST_CASE(ends_with_relation) +{ + attrs::constant< std::string > attr1("abcdABCD"); + attr_set set1, set2, set3; + + set1["MyStr"] = attr1; + attr_values values1(set1, set2, set3); + values1.freeze(); + + { + logging::filter f = logging::parse_filter("%MyStr% ends_with \"abcd\""); + BOOST_CHECK(!f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% ends_with \"ABCD\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% ends_with \"efgh\""); + BOOST_CHECK(!f(values1)); + } +} + +// Tests for contains relation filter +BOOST_AUTO_TEST_CASE(contains_relation) +{ + attrs::constant< std::string > attr1("abcdABCD"); + attr_set set1, set2, set3; + + set1["MyStr"] = attr1; + attr_values values1(set1, set2, set3); + values1.freeze(); + + { + logging::filter f = logging::parse_filter("%MyStr% contains \"abcd\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% contains \"ABCD\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% contains \"cdAB\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% contains \"efgh\""); + BOOST_CHECK(!f(values1)); + } +} + +// Tests for regex matching relation filter +BOOST_AUTO_TEST_CASE(matches_relation) +{ + attrs::constant< std::string > attr1("hello"); + attrs::constant< std::string > attr2("127.0.0.1"); + attr_set set1, set2, set3; + + set1["MyStr"] = attr1; + set1["MyIP"] = attr2; + attr_values values1(set1, set2, set3); + values1.freeze(); + + { + logging::filter f = logging::parse_filter("%MyStr% matches \"h.*\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% matches \"w.*\""); + BOOST_CHECK(!f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% matches \"\\\\d*\""); + BOOST_CHECK(!f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyStr% matches \"[a-z]*\""); + BOOST_CHECK(f(values1)); + } + { + logging::filter f = logging::parse_filter("%MyIP% matches \"\\\\d*\\\\.\\\\d*\\\\.\\\\d*\\\\.\\\\d*\""); + BOOST_CHECK(f(values1)); + } +} + +namespace { + +class test_filter_factory : + public logging::filter_factory< char > +{ +private: + typedef logging::filter_factory< char > base_type; + +public: + enum relation_type + { + custom, + exists, + equality, + inequality, + less, + greater, + less_or_equal, + greater_or_equal + }; + + typedef base_type::string_type string_type; + +public: + explicit test_filter_factory(logging::attribute_name const& name) : m_name(name), m_rel(custom), m_called(false) + { + } + + void expect_relation(relation_type rel, string_type const& arg) + { + m_rel = rel; + m_arg = arg; + m_custom_rel.clear(); + } + + void expect_relation(string_type const& rel, string_type const& arg) + { + m_rel = custom; + m_arg = arg; + m_custom_rel = rel; + } + + void check_called() + { + BOOST_CHECK(m_called); + m_called = false; + } + + logging::filter on_exists_test(logging::attribute_name const& name) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, exists); + m_called = true; + return logging::filter(); + } + logging::filter on_equality_relation(logging::attribute_name const& name, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, equality); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + logging::filter on_inequality_relation(logging::attribute_name const& name, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, inequality); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + logging::filter on_less_relation(logging::attribute_name const& name, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, less); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + logging::filter on_greater_relation(logging::attribute_name const& name, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, greater); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + logging::filter on_less_or_equal_relation(logging::attribute_name const& name, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, less_or_equal); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + logging::filter on_greater_or_equal_relation(logging::attribute_name const& name, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, greater_or_equal); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + logging::filter on_custom_relation(logging::attribute_name const& name, string_type const& rel, string_type const& arg) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_rel, custom); + BOOST_CHECK_EQUAL(m_custom_rel, rel); + BOOST_CHECK_EQUAL(m_arg, arg); + m_called = true; + return logging::filter(); + } + +private: + logging::attribute_name m_name; + relation_type m_rel; + string_type m_arg; + string_type m_custom_rel; + bool m_called; +}; + +} // namespace + +// Tests for filter factory +BOOST_AUTO_TEST_CASE(filter_factory) +{ + logging::attribute_name attr_name("MyCustomAttr"); + boost::shared_ptr< test_filter_factory > factory(new test_filter_factory(attr_name)); + logging::register_filter_factory(attr_name, factory); + + BOOST_TEST_CHECKPOINT("filter_factory::exists"); + factory->expect_relation(test_filter_factory::exists, ""); + logging::parse_filter("%MyCustomAttr%"); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::equality"); + factory->expect_relation(test_filter_factory::equality, "15"); + logging::parse_filter("%MyCustomAttr% = 15"); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::equality"); + factory->expect_relation(test_filter_factory::equality, "hello"); + logging::parse_filter("%MyCustomAttr% = hello"); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::equality"); + factory->expect_relation(test_filter_factory::equality, "hello"); + logging::parse_filter("%MyCustomAttr% = \"hello\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::equality"); + factory->expect_relation(test_filter_factory::equality, "hello\nworld"); + logging::parse_filter("%MyCustomAttr% = \"hello\\nworld\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::inequality"); + factory->expect_relation(test_filter_factory::inequality, "hello"); + logging::parse_filter("%MyCustomAttr% != \"hello\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::less"); + factory->expect_relation(test_filter_factory::less, "hello"); + logging::parse_filter("%MyCustomAttr% < \"hello\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::greater"); + factory->expect_relation(test_filter_factory::greater, "hello"); + logging::parse_filter("%MyCustomAttr% > \"hello\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::less_or_equal"); + factory->expect_relation(test_filter_factory::less_or_equal, "hello"); + logging::parse_filter("%MyCustomAttr% <= \"hello\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::greater_or_equal"); + factory->expect_relation(test_filter_factory::greater_or_equal, "hello"); + logging::parse_filter("%MyCustomAttr% >= \"hello\""); + factory->check_called(); + + BOOST_TEST_CHECKPOINT("filter_factory::custom"); + factory->expect_relation("my_relation", "hello"); + logging::parse_filter("%MyCustomAttr% my_relation \"hello\""); + factory->check_called(); +} + +// Tests for invalid filters +BOOST_AUTO_TEST_CASE(invalid) +{ + BOOST_CHECK_THROW(logging::parse_filter("%MyStr"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("MyStr%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%MyStr% abcd"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("(%MyStr%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%MyStr%)"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("!"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("!()"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("\"xxx\" == %MyStr%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%MyStr% == \"xxx"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%MyStr% === \"xxx\""), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%MyStr% ! \"xxx\""), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_filter("%MyStr% %MyStr2%"), logging::parse_error); +} + +#endif // !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) diff --git a/src/boost/libs/log/test/run/util_stp_formatter_parser.cpp b/src/boost/libs/log/test/run/util_stp_formatter_parser.cpp new file mode 100644 index 00000000..15a60367 --- /dev/null +++ b/src/boost/libs/log/test/run/util_stp_formatter_parser.cpp @@ -0,0 +1,257 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 setup_formatter_parser.cpp + * \author Andrey Semashev + * \date 25.08.2013 + * + * \brief This header contains tests for the formatter parser. + */ + +#define BOOST_TEST_MODULE setup_formatter_parser + +#include <string> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/setup/formatter_parser.hpp> + +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + +#include <boost/smart_ptr/shared_ptr.hpp> +#include <boost/log/exceptions.hpp> +#include <boost/log/core/record.hpp> +#include <boost/log/core/record_view.hpp> +#include <boost/log/attributes/constant.hpp> +#include <boost/log/attributes/attribute_set.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include <boost/log/utility/formatting_ostream.hpp> +#include <boost/log/expressions/formatter.hpp> +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; + +typedef logging::attribute_set attr_set; +typedef logging::attribute_value_set attr_values; +typedef logging::record_view record_view; + +typedef logging::basic_formatting_ostream< char > osstream; +typedef logging::basic_formatter< char > formatter; + +// Tests for simple attribute placeholders in formatters +BOOST_AUTO_TEST_CASE(attr_placeholders) +{ + attrs::constant< int > attr1(10); + attrs::constant< std::string > attr2("hello"); + attr_set set1; + + set1["MyAttr"] = attr1; + set1["MyStr"] = attr2; + + record_view rec = make_record_view(set1); + + { + formatter f = logging::parse_formatter("String literal"); + std::string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << "String literal"; + BOOST_CHECK_EQUAL(strm1.str(), strm2.str()); + } + { + formatter f = logging::parse_formatter("MyAttr: %MyAttr%, MyStr: %MyStr%"); + std::string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << "MyAttr: " << attr1.get() << ", MyStr: " << attr2.get(); + BOOST_CHECK_EQUAL(strm1.str(), strm2.str()); + } + { + formatter f = logging::parse_formatter("MyAttr: % MyAttr %, MyStr: % MyStr %"); + std::string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << "MyAttr: " << attr1.get() << ", MyStr: " << attr2.get(); + BOOST_CHECK_EQUAL(strm1.str(), strm2.str()); + } + { + formatter f = logging::parse_formatter("%MyAttr%%MyStr%"); + std::string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << attr1.get() << attr2.get(); + BOOST_CHECK_EQUAL(strm1.str(), strm2.str()); + } + { + formatter f = logging::parse_formatter("MyAttr: %MyAttr%, MissingAttr: %MissingAttr%"); + std::string str1, str2; + osstream strm1(str1), strm2(str2); + f(rec, strm1); + strm2 << "MyAttr: " << attr1.get() << ", MissingAttr: "; + BOOST_CHECK_EQUAL(strm1.str(), strm2.str()); + } +} + +namespace { + +class test_formatter_factory : + public logging::formatter_factory< char > +{ +private: + typedef logging::formatter_factory< char > base_type; + +public: + typedef base_type::string_type string_type; + typedef base_type::args_map args_map; + typedef base_type::formatter_type formatter_type; + +public: + explicit test_formatter_factory(logging::attribute_name const& name) : m_name(name), m_called(false) + { + } + + void expect_args(args_map const& args) + { + m_args = args; + } + + void check_called() + { + BOOST_CHECK(m_called); + m_called = false; + } + + formatter_type create_formatter(logging::attribute_name const& name, args_map const& args) + { + BOOST_CHECK_EQUAL(m_name, name); + BOOST_CHECK_EQUAL(m_args.size(), args.size()); + + for (args_map::const_iterator it = m_args.begin(), end = m_args.end(); it != end; ++it) + { + args_map::const_iterator parsed_it = args.find(it->first); + if (parsed_it != args.end()) + { + BOOST_TEST_CHECKPOINT(("Arg: " + it->first)); + BOOST_CHECK_EQUAL(it->second, parsed_it->second); + } + else + { + BOOST_ERROR(("Arg not found: " + it->first)); + } + } + + m_called = true; + return formatter_type(); + } + +private: + logging::attribute_name m_name; + args_map m_args; + bool m_called; +}; + +} // namespace + +// Tests for formatter factory +BOOST_AUTO_TEST_CASE(formatter_factory) +{ + logging::attribute_name attr_name("MyCustomAttr"); + boost::shared_ptr< test_formatter_factory > factory(new test_formatter_factory(attr_name)); + logging::register_formatter_factory(attr_name, factory); + + { + BOOST_TEST_CHECKPOINT("formatter 1"); + test_formatter_factory::args_map args; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr%"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 2"); + test_formatter_factory::args_map args; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr()%"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 3"); + test_formatter_factory::args_map args; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr ( ) %"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 4"); + test_formatter_factory::args_map args; + args["param1"] = "10"; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr(param1=10)%"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 5"); + test_formatter_factory::args_map args; + args["param1"] = "10"; + factory->expect_args(args); + logging::parse_formatter("Hello: % MyCustomAttr ( param1 = 10 ) % "); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 6"); + test_formatter_factory::args_map args; + args["param1"] = " 10 "; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr(param1=\" 10 \")%"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 7"); + test_formatter_factory::args_map args; + args["param1"] = "10"; + args["param2"] = "abcd"; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr(param1 = 10, param2 = abcd)%"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 8"); + test_formatter_factory::args_map args; + args["param1"] = "10"; + args["param2"] = "abcd"; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr(param1=10,param2=abcd)%"); + factory->check_called(); + } + { + BOOST_TEST_CHECKPOINT("formatter 9"); + test_formatter_factory::args_map args; + args["param1"] = "10"; + args["param2"] = "abcd"; + args["param_last"] = "-2.2"; + factory->expect_args(args); + logging::parse_formatter("Hello: %MyCustomAttr(param1 = 10, param2 = \"abcd\", param_last = -2.2)%"); + factory->check_called(); + } +} + +// Tests for invalid formatters +BOOST_AUTO_TEST_CASE(invalid) +{ + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("MyStr%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr)%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param=%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param)%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param=)%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(=value)%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param,param2)%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param=value,)%"), logging::parse_error); + BOOST_CHECK_THROW(logging::parse_formatter("%MyStr(param=value,param2)%"), logging::parse_error); +} + +#endif // !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) && !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) diff --git a/src/boost/libs/log/test/run/util_stp_settings_parser.cpp b/src/boost/libs/log/test/run/util_stp_settings_parser.cpp new file mode 100644 index 00000000..1027fb5b --- /dev/null +++ b/src/boost/libs/log/test/run/util_stp_settings_parser.cpp @@ -0,0 +1,294 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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 setup_settings_parser.cpp + * \author Andrey Semashev + * \date 25.08.2013 + * + * \brief This header contains tests for the settings parser. + */ + +#define BOOST_TEST_MODULE setup_settings_parser + +#include <string> +#include <sstream> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/setup/settings_parser.hpp> + +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) + +#include <boost/log/exceptions.hpp> + +namespace logging = boost::log; + +typedef logging::basic_settings< char > settings; + +// Tests for single-level settings +BOOST_AUTO_TEST_CASE(single_level) +{ + { + std::istringstream strm + ( + "[Section1]\n" + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + settings s = logging::parse_settings(strm); + + BOOST_CHECK(s.has_section("Section1")); + BOOST_CHECK(s.has_section("Section2")); + BOOST_CHECK(!s.has_section("Section3")); + + BOOST_CHECK(s.has_parameter("Section1", "Param1")); + BOOST_CHECK(s.has_parameter("Section1", "Param2")); + + BOOST_CHECK(s.has_parameter("Section2", "Param1")); + BOOST_CHECK(s.has_parameter("Section2", "Param2")); + + BOOST_CHECK_EQUAL(s["Section1"]["Param1"].or_default(std::string()), "Value1"); + BOOST_CHECK_EQUAL(s["Section1"]["Param2"].or_default(std::string()), "hello, \"world\""); + + BOOST_CHECK_EQUAL(s["Section2"]["Param1"].or_default(std::string()), "10"); + BOOST_CHECK_EQUAL(s["Section2"]["Param2"].or_default(std::string()), "-2.2"); + } +} + +// Tests for multi-level settings +BOOST_AUTO_TEST_CASE(multi_level) +{ + { + std::istringstream strm + ( + " [Section1]\n" + "\n" + "Param1 = Value1 \n" + "Param2=\"hello, \\\"world\\\"\" \n" + "\n" + "[Section1.Subsection2] \n" + "\n" + "Param1=10\n" + "Param2=-2.2\n" + ); + settings s = logging::parse_settings(strm); + + BOOST_CHECK(s.has_section("Section1")); + BOOST_CHECK(s.has_section("Section1.Subsection2")); + BOOST_CHECK(!s.has_section("Subsection2")); + + BOOST_CHECK(s.has_parameter("Section1", "Param1")); + BOOST_CHECK(s.has_parameter("Section1", "Param2")); + + BOOST_CHECK(s.has_parameter("Section1.Subsection2", "Param1")); + BOOST_CHECK(s.has_parameter("Section1.Subsection2", "Param2")); + BOOST_CHECK(!s.has_parameter("Subsection2", "Param1")); + BOOST_CHECK(!s.has_parameter("Subsection2", "Param2")); + + BOOST_CHECK_EQUAL(s["Section1"]["Param1"].or_default(std::string()), "Value1"); + BOOST_CHECK_EQUAL(s["Section1"]["Param2"].or_default(std::string()), "hello, \"world\""); + + BOOST_CHECK_EQUAL(s["Section1.Subsection2"]["Param1"].or_default(std::string()), "10"); + BOOST_CHECK_EQUAL(s["Section1.Subsection2"]["Param2"].or_default(std::string()), "-2.2"); + } +} + +// Tests for comments +BOOST_AUTO_TEST_CASE(comments) +{ + { + std::istringstream strm + ( + "# Some comment\n" + "[ Section1 ] # another comment\n" + "\n" + "Param1 = Value1 ### yet another comment \n" + "Param2=\"hello, \\\"world\\\"\" # comment after a quoted string\n" + "\n" + "[ Section2 ]\n" + "\n" + "Param1=10#comment after a number\n" + "Param2=-2.2#comment without a terminating newline" + "\n" + "#[Section3]\n" + "#\n" + "#Param1=10#comment after a number\n" + "#Param2=-2.2#comment without a terminating newline" + ); + settings s = logging::parse_settings(strm); + + BOOST_CHECK(s.has_section("Section1")); + BOOST_CHECK(s.has_section("Section2")); + BOOST_CHECK(!s.has_section("Section3")); + + BOOST_CHECK(s.has_parameter("Section1", "Param1")); + BOOST_CHECK(s.has_parameter("Section1", "Param2")); + + BOOST_CHECK(s.has_parameter("Section2", "Param1")); + BOOST_CHECK(s.has_parameter("Section2", "Param2")); + + BOOST_CHECK_EQUAL(s["Section1"]["Param1"].or_default(std::string()), "Value1"); + BOOST_CHECK_EQUAL(s["Section1"]["Param2"].or_default(std::string()), "hello, \"world\""); + + BOOST_CHECK_EQUAL(s["Section2"]["Param1"].or_default(std::string()), "10"); + BOOST_CHECK_EQUAL(s["Section2"]["Param2"].or_default(std::string()), "-2.2"); + } +} + +// Tests for invalid settings +BOOST_AUTO_TEST_CASE(invalid) +{ + { + std::istringstream strm + ( + "Param1 = Value1\n" // parameters outside sections + "Param2 = \"hello, \\\"world\\\"\"\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1\n" // missing closing brace + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "Section1]\n" // missing opening brace + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1=xyz]\n" // invalid characters in the section name + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1# hello?]\n" // invalid characters in the section name + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "(Section1)\n" // invalid braces + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1]\n" + "\n" + "Param1 =\n" // no parameter value + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1]\n" + "\n" + "Param1\n" // no parameter value + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1]\n" + "\n" + "Param1 = Value1\n" + "Param2 = \"hello, \\\"world\\\"\n" // unterminated quote + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } + { + std::istringstream strm + ( + "[Section1]\n" + "\n" + "Param1 = Value1 Value2\n" // multi-word value + "Param2 = \"hello, \\\"world\\\"\"\n" + "\n" + "[Section2]\n" + "\n" + "Param1 = 10\n" + "Param2 = -2.2\n" + ); + BOOST_CHECK_THROW(logging::parse_settings(strm), logging::parse_error); + } +} + +#endif // !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) diff --git a/src/boost/libs/log/test/run/util_string_literal.cpp b/src/boost/libs/log/test/run/util_string_literal.cpp new file mode 100644 index 00000000..356f8737 --- /dev/null +++ b/src/boost/libs/log/test/run/util_string_literal.cpp @@ -0,0 +1,221 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * 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_string_literal.cpp + * \author Andrey Semashev + * \date 09.01.2009 + * + * \brief This header contains tests for the string literals wrapper. + */ + +#define BOOST_TEST_MODULE util_string_literal + +#include <cwchar> +#include <cstring> +#include <set> +#include <memory> +#include <string> +#include <algorithm> +#include <boost/test/unit_test.hpp> +#include <boost/log/utility/string_literal.hpp> + +namespace logging = boost::log; + +// Construction tests +BOOST_AUTO_TEST_CASE(string_literal_ctors) +{ + // Default construction + { + logging::string_literal lit; + BOOST_CHECK(lit.empty()); + BOOST_CHECK_EQUAL(lit.size(), 0UL); + BOOST_CHECK(lit.c_str() != NULL); + } + + // Construction from a literal + { + logging::string_literal lit = "abcd"; + BOOST_CHECK(!lit.empty()); + BOOST_CHECK_EQUAL(lit.size(), 4UL); + BOOST_CHECK(std::strcmp(lit.c_str(), "abcd") == 0); + } + +#ifdef BOOST_LOG_USE_WCHAR_T + // Copying + { + logging::wstring_literal lit1 = L"Hello"; + logging::wstring_literal lit2 = lit1; + BOOST_CHECK(std::wcscmp(lit2.c_str(), L"Hello") == 0); + BOOST_CHECK(std::wcscmp(lit1.c_str(), lit2.c_str()) == 0); + } + + // Generator functions + { + logging::string_literal lit1 = logging::str_literal("Wow!"); + BOOST_CHECK(std::strcmp(lit1.c_str(), "Wow!") == 0); + + logging::wstring_literal lit2 = logging::str_literal(L"Wow!"); + BOOST_CHECK(std::wcscmp(lit2.c_str(), L"Wow!") == 0); + } +#endif +} + +// Assignment tests +BOOST_AUTO_TEST_CASE(string_literal_assignment) +{ + // operator= + { + logging::string_literal lit; + BOOST_CHECK(lit.empty()); + + lit = "Hello"; + BOOST_CHECK(std::strcmp(lit.c_str(), "Hello") == 0); + + logging::string_literal empty_lit; + lit = empty_lit; + BOOST_CHECK(lit.empty()); + + logging::string_literal filled_lit = "Some string"; + lit = filled_lit; + BOOST_CHECK(std::strcmp(lit.c_str(), filled_lit.c_str()) == 0); + } + + // assign + { + logging::string_literal lit; + BOOST_CHECK(lit.empty()); + + lit.assign("Hello"); + BOOST_CHECK(std::strcmp(lit.c_str(), "Hello") == 0); + + logging::string_literal empty_lit; + lit.assign(empty_lit); + BOOST_CHECK(lit.empty()); + + logging::string_literal filled_lit = "Some string"; + lit.assign(filled_lit); + BOOST_CHECK(std::strcmp(lit.c_str(), filled_lit.c_str()) == 0); + } +} + +// Comparison tests +BOOST_AUTO_TEST_CASE(string_literal_comparison) +{ + logging::string_literal lit; + BOOST_CHECK(lit == ""); + + lit = "abcdefg"; + BOOST_CHECK(lit == "abcdefg"); + BOOST_CHECK(lit != "xyz"); + BOOST_CHECK(lit != "aBcDeFg"); + + logging::string_literal lit2 = "Yo!"; + BOOST_CHECK(lit != lit2); + lit2 = "abcdefg"; + BOOST_CHECK(lit == lit2); + + BOOST_CHECK(lit.compare(lit2) == 0); + BOOST_CHECK(lit.compare("aaaaa") > 0); + BOOST_CHECK(lit.compare("zzzzzzzz") < 0); + BOOST_CHECK(lit.compare("zbcdefg") < 0); + + BOOST_CHECK(lit.compare(2, 3, "cde") == 0); + BOOST_CHECK(lit.compare(2, 3, "cdefgh", 3) == 0); + + // Check ordering + std::set< logging::string_literal > lit_set; + lit_set.insert(logging::str_literal("abc")); + lit_set.insert(logging::str_literal("def")); + lit_set.insert(logging::str_literal("aaa")); + lit_set.insert(logging::str_literal("abcd")); + lit_set.insert(logging::str_literal("zz")); + + std::set< std::string > str_set; + str_set.insert(logging::str_literal("abc").str()); + str_set.insert(logging::str_literal("def").str()); + str_set.insert(logging::str_literal("aaa").str()); + str_set.insert(logging::str_literal("abcd").str()); + str_set.insert(logging::str_literal("zz").str()); + + BOOST_CHECK_EQUAL_COLLECTIONS(lit_set.begin(), lit_set.end(), str_set.begin(), str_set.end()); +} + +// Iteration tests +BOOST_AUTO_TEST_CASE(string_literal_iteration) +{ + std::string str; + logging::string_literal lit = "abcdefg"; + + std::copy(lit.begin(), lit.end(), std::back_inserter(str)); + BOOST_CHECK(str == "abcdefg"); + + str.clear(); + std::copy(lit.rbegin(), lit.rend(), std::back_inserter(str)); + BOOST_CHECK(str == "gfedcba"); +} + +// Subscript tests +BOOST_AUTO_TEST_CASE(string_literal_indexing) +{ + logging::string_literal lit = "abcdefg"; + + BOOST_CHECK_EQUAL(lit[2], 'c'); + BOOST_CHECK_EQUAL(lit.at(3), 'd'); + + BOOST_CHECK_THROW(lit.at(100), std::exception); +} + +// Clearing tests +BOOST_AUTO_TEST_CASE(string_literal_clear) +{ + logging::string_literal lit = "yo-ho-ho"; + BOOST_CHECK(!lit.empty()); + + lit.clear(); + BOOST_CHECK(lit.empty()); + BOOST_CHECK_EQUAL(lit, ""); +} + +// Swapping tests +BOOST_AUTO_TEST_CASE(string_literal_swap) +{ + logging::string_literal lit1 = "yo-ho-ho"; + logging::string_literal lit2 = "hello"; + + lit1.swap(lit2); + BOOST_CHECK_EQUAL(lit1, "hello"); + BOOST_CHECK_EQUAL(lit2, "yo-ho-ho"); + + swap(lit1, lit2); + BOOST_CHECK_EQUAL(lit2, "hello"); + BOOST_CHECK_EQUAL(lit1, "yo-ho-ho"); +} + +// STL strings acquisition tests +BOOST_AUTO_TEST_CASE(string_literal_str) +{ + logging::string_literal lit = "yo-ho-ho"; + std::string str = lit.str(); + BOOST_CHECK_EQUAL(str, "yo-ho-ho"); + BOOST_CHECK_EQUAL(lit, str); +} + +// Substring extraction tests +BOOST_AUTO_TEST_CASE(string_literal_copy) +{ + logging::string_literal lit = "yo-ho-ho"; + char t[32] = { 0 }; + + BOOST_CHECK_EQUAL(lit.copy(t, 2), 2UL); + BOOST_CHECK(std::strcmp(t, "yo") == 0); + + BOOST_CHECK_EQUAL(lit.copy(t, 4, 2), 4UL); + BOOST_CHECK(std::strcmp(t, "-ho-") == 0); + + BOOST_CHECK_EQUAL(lit.copy(t, 100), 8UL); + BOOST_CHECK(std::strcmp(t, "yo-ho-ho") == 0); +} |