diff --git a/Readme.md b/Readme.md index 430e582..92ea758 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,7 @@ Boost libraries - trimmed down for Citra ======================================== -This is a subset of Boost v1.71.0 generated using the bcp tool. To get a list of boost modules guaranteed to exist, check the build script. +This is a subset of Boost v1.82.0 generated using the bcp tool. To get a list of boost modules guaranteed to exist, check the build script. Updating this repo (on Windows) =============================== diff --git a/boost/locale/config.hpp b/boost/locale/config.hpp new file mode 100644 index 0000000..821ba7f --- /dev/null +++ b/boost/locale/config.hpp @@ -0,0 +1,77 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_CONFIG_HPP_INCLUDED +#define BOOST_LOCALE_CONFIG_HPP_INCLUDED + +#include + +#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_LOCALE_DYN_LINK) +# ifdef BOOST_LOCALE_SOURCE +# define BOOST_LOCALE_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_LOCALE_DECL BOOST_SYMBOL_IMPORT +# endif // BOOST_LOCALE_SOURCE +#else +# define BOOST_LOCALE_DECL +#endif // BOOST_LOCALE_DYN_LINK + +// +// Automatically link to the correct build variant where possible. +// +#if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_LOCALE_NO_LIB) && !defined(BOOST_LOCALE_SOURCE) +// +// Set the name of our library, this will get undef'ed by auto_link.hpp +// once it's done with it: +// +# define BOOST_LIB_NAME boost_locale +// +// If we're importing code from a dll, then tell auto_link.hpp about it: +// +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_LOCALE_DYN_LINK) +# define BOOST_DYN_LINK +# endif +// +// And include the header that does the work: +// +# include +#endif // auto-linking disabled + +// Check for some C++11 features to provide easier checks for what is missing +// shortly after the requirement of C++11 in Boost 1.81 +// clang-format off +#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || \ + defined(BOOST_NO_CXX11_DEFAULTED_MOVES) || \ + defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) || \ + defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) || \ + defined(BOOST_NO_CXX11_NOEXCEPT) || \ + defined(BOOST_NO_CXX11_OVERRIDE) || \ + defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \ + defined(BOOST_NO_CXX11_SMART_PTR) || \ + defined(BOOST_NO_CXX11_STATIC_ASSERT) +// clang-format on +# error "Boost.Locale requires C++11 since Boost 1.81." +#endif + +#ifdef _MSC_VER +// Denote a constant condition, e.g. for if(sizeof(... +# define BOOST_LOCALE_START_CONST_CONDITION __pragma(warning(push)) __pragma(warning(disable : 4127)) +# define BOOST_LOCALE_END_CONST_CONDITION __pragma(warning(pop)) +#else +# define BOOST_LOCALE_START_CONST_CONDITION +# define BOOST_LOCALE_END_CONST_CONDITION +#endif + +/// \cond INTERNAL +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +// Internal define to check if we have access to the Win32 API +# define BOOST_LOCALE_USE_WIN32_API 1 +#else +# define BOOST_LOCALE_USE_WIN32_API 0 +#endif +/// \endcond + +#endif // boost/locale/config.hpp diff --git a/boost/locale/encoding_errors.hpp b/boost/locale/encoding_errors.hpp new file mode 100644 index 0000000..6643a63 --- /dev/null +++ b/boost/locale/encoding_errors.hpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_ENCODING_ERRORS_HPP_INCLUDED +#define BOOST_LOCALE_ENCODING_ERRORS_HPP_INCLUDED + +#include +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable : 4275 4251 4231 4660) +#endif + +namespace boost { namespace locale { namespace conv { + /// \addtogroup codepage + /// + /// @{ + + /// \brief The exception that is thrown in case of conversion error + class BOOST_SYMBOL_VISIBLE conversion_error : public std::runtime_error { + public: + conversion_error() : std::runtime_error("Conversion failed") {} + }; + + /// \brief This exception is thrown in case of use of unsupported + /// or invalid character set + class BOOST_SYMBOL_VISIBLE invalid_charset_error : public std::runtime_error { + public: + /// Create an error for charset \a charset + invalid_charset_error(const std::string& charset) : + std::runtime_error("Invalid or unsupported charset:" + charset) + {} + }; + + /// enum that defines conversion policy + enum method_type { + skip = 0, ///< Skip illegal/unconvertible characters + stop = 1, ///< Stop conversion and throw conversion_error + default_method = skip ///< Default method - skip + }; + + /// @} + +}}} // namespace boost::locale::conv + +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +#endif diff --git a/boost/locale/encoding_utf.hpp b/boost/locale/encoding_utf.hpp new file mode 100644 index 0000000..ed1e5db --- /dev/null +++ b/boost/locale/encoding_utf.hpp @@ -0,0 +1,68 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_ENCODING_UTF_HPP_INCLUDED +#define BOOST_LOCALE_ENCODING_UTF_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable : 4275 4251 4231 4660) +#endif + +namespace boost { namespace locale { namespace conv { + /// \addtogroup codepage + /// + /// @{ + + /// Convert a Unicode text in range [begin,end) to other Unicode encoding + template + std::basic_string utf_to_utf(const CharIn* begin, const CharIn* end, method_type how = default_method) + { + std::basic_string result; + result.reserve(end - begin); + typedef std::back_insert_iterator> inserter_type; + inserter_type inserter(result); + utf::code_point c; + while(begin != end) { + c = utf::utf_traits::template decode(begin, end); + if(c == utf::illegal || c == utf::incomplete) { + if(how == stop) + throw conversion_error(); + } else { + utf::utf_traits::template encode(c, inserter); + } + } + return result; + } + + /// Convert a Unicode NULL terminated string \a str other Unicode encoding + template + std::basic_string utf_to_utf(const CharIn* str, method_type how = default_method) + { + return utf_to_utf(str, util::str_end(str), how); + } + + /// Convert a Unicode string \a str other Unicode encoding + template + std::basic_string utf_to_utf(const std::basic_string& str, method_type how = default_method) + { + return utf_to_utf(str.c_str(), str.c_str() + str.size(), how); + } + + /// @} + +}}} // namespace boost::locale::conv + +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +#endif diff --git a/boost/locale/utf.hpp b/boost/locale/utf.hpp new file mode 100644 index 0000000..841b10f --- /dev/null +++ b/boost/locale/utf.hpp @@ -0,0 +1,371 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_UTF_HPP_INCLUDED +#define BOOST_LOCALE_UTF_HPP_INCLUDED + +#include +#include + +namespace boost { namespace locale { + /// \brief Namespace that holds basic operations on UTF encoded sequences + /// + /// All functions defined in this namespace do not require linking with Boost.Locale library + namespace utf { + /// \brief The integral type that can hold a Unicode code point + typedef uint32_t code_point; + + /// \brief Special constant that defines illegal code point + constexpr code_point illegal = 0xFFFFFFFFu; + /// \brief Special constant that defines incomplete code point + constexpr code_point incomplete = 0xFFFFFFFEu; + + /// \brief the function checks if \a v is a valid code point + inline bool is_valid_codepoint(code_point v) + { + if(v > 0x10FFFF) + return false; + if(0xD800 <= v && v <= 0xDFFF) // surrogates + return false; + return true; + } + +#ifdef BOOST_LOCALE_DOXYGEN + + /// \brief UTF Traits class - functions to convert UTF sequences to and from Unicode code points + template + struct utf_traits { + /// The type of the character + typedef CharType char_type; + + /// Read one code point from the range [p,e) and return it. + /// + /// - If the sequence that was read is incomplete sequence returns \ref incomplete, + /// - If illegal sequence detected returns \ref illegal + /// + /// Requirements + /// + /// - Iterator is valid input iterator + /// + /// Postconditions + /// + /// - p points to the last consumed character + template + static code_point decode(Iterator& p, Iterator e); + + /// Maximal width of valid sequence in the code units: + /// + /// - UTF-8 - 4 + /// - UTF-16 - 2 + /// - UTF-32 - 1 + static constexpr int max_width; + + /// The width of specific code point in the code units. + /// + /// Requirement: value is a valid Unicode code point + /// Returns value in range [1..max_width] + static int width(code_point value); + + /// Get the size of the trail part of variable length encoded sequence. + /// + /// Returns -1 if C is not valid lead character + static int trail_length(char_type c); + /// Returns true if c is trail code unit, always false for UTF-32 + static bool is_trail(char_type c); + /// Returns true if c is lead code unit, always true of UTF-32 + static bool is_lead(char_type c); + + /// Convert valid Unicode code point \a value to the UTF sequence. + /// + /// Requirements: + /// + /// - \a value is valid code point + /// - \a out is an output iterator should be able to accept at least width(value) units + /// + /// Returns the iterator past the last written code unit. + template + static Iterator encode(code_point value, Iterator out); + + /// Decodes valid UTF sequence that is pointed by p into code point. + /// + /// If the sequence is invalid or points to end the behavior is undefined + template + static code_point decode_valid(Iterator& p); + }; + +#else + + template + struct utf_traits; + + template + struct utf_traits { + typedef CharType char_type; + + static int trail_length(char_type ci) + { + unsigned char c = ci; + if(c < 128) + return 0; + if(BOOST_UNLIKELY(c < 194)) + return -1; + if(c < 224) + return 1; + if(c < 240) + return 2; + if(BOOST_LIKELY(c <= 244)) + return 3; + return -1; + } + + static constexpr int max_width = 4; + + static int width(code_point value) + { + if(value <= 0x7F) { + return 1; + } else if(value <= 0x7FF) { + return 2; + } else if(BOOST_LIKELY(value <= 0xFFFF)) { + return 3; + } else { + return 4; + } + } + + static bool is_trail(char_type ci) + { + unsigned char c = ci; + return (c & 0xC0) == 0x80; + } + + static bool is_lead(char_type ci) { return !is_trail(ci); } + + template + static code_point decode(Iterator& p, Iterator e) + { + if(BOOST_UNLIKELY(p == e)) + return incomplete; + + unsigned char lead = *p++; + + // First byte is fully validated here + int trail_size = trail_length(lead); + + if(BOOST_UNLIKELY(trail_size < 0)) + return illegal; + + // Ok as only ASCII may be of size = 0 + // also optimize for ASCII text + if(trail_size == 0) + return lead; + + code_point c = lead & ((1 << (6 - trail_size)) - 1); + + // Read the rest + unsigned char tmp; + switch(trail_size) { + case 3: + if(BOOST_UNLIKELY(p == e)) + return incomplete; + tmp = *p++; + if(!is_trail(tmp)) + return illegal; + c = (c << 6) | (tmp & 0x3F); + BOOST_FALLTHROUGH; + case 2: + if(BOOST_UNLIKELY(p == e)) + return incomplete; + tmp = *p++; + if(!is_trail(tmp)) + return illegal; + c = (c << 6) | (tmp & 0x3F); + BOOST_FALLTHROUGH; + case 1: + if(BOOST_UNLIKELY(p == e)) + return incomplete; + tmp = *p++; + if(!is_trail(tmp)) + return illegal; + c = (c << 6) | (tmp & 0x3F); + } + + // Check code point validity: no surrogates and + // valid range + if(BOOST_UNLIKELY(!is_valid_codepoint(c))) + return illegal; + + // make sure it is the most compact representation + if(BOOST_UNLIKELY(width(c) != trail_size + 1)) + return illegal; + + return c; + } + + template + static code_point decode_valid(Iterator& p) + { + unsigned char lead = *p++; + if(lead < 192) + return lead; + + int trail_size; + + if(lead < 224) + trail_size = 1; + else if(BOOST_LIKELY(lead < 240)) // non-BMP rare + trail_size = 2; + else + trail_size = 3; + + code_point c = lead & ((1 << (6 - trail_size)) - 1); + + switch(trail_size) { + case 3: c = (c << 6) | (static_cast(*p++) & 0x3F); BOOST_FALLTHROUGH; + case 2: c = (c << 6) | (static_cast(*p++) & 0x3F); BOOST_FALLTHROUGH; + case 1: c = (c << 6) | (static_cast(*p++) & 0x3F); + } + + return c; + } + + template + static Iterator encode(code_point value, Iterator out) + { + if(value <= 0x7F) { + *out++ = static_cast(value); + } else if(value <= 0x7FF) { + *out++ = static_cast((value >> 6) | 0xC0); + *out++ = static_cast((value & 0x3F) | 0x80); + } else if(BOOST_LIKELY(value <= 0xFFFF)) { + *out++ = static_cast((value >> 12) | 0xE0); + *out++ = static_cast(((value >> 6) & 0x3F) | 0x80); + *out++ = static_cast((value & 0x3F) | 0x80); + } else { + *out++ = static_cast((value >> 18) | 0xF0); + *out++ = static_cast(((value >> 12) & 0x3F) | 0x80); + *out++ = static_cast(((value >> 6) & 0x3F) | 0x80); + *out++ = static_cast((value & 0x3F) | 0x80); + } + return out; + } + }; // utf8 + + template + struct utf_traits { + typedef CharType char_type; + + // See RFC 2781 + static bool is_first_surrogate(uint16_t x) { return 0xD800 <= x && x <= 0xDBFF; } + static bool is_second_surrogate(uint16_t x) { return 0xDC00 <= x && x <= 0xDFFF; } + static code_point combine_surrogate(uint16_t w1, uint16_t w2) + { + return ((code_point(w1 & 0x3FF) << 10) | (w2 & 0x3FF)) + 0x10000; + } + static int trail_length(char_type c) + { + if(is_first_surrogate(c)) + return 1; + if(is_second_surrogate(c)) + return -1; + return 0; + } + + /// Returns true if c is trail code unit, always false for UTF-32 + static bool is_trail(char_type c) { return is_second_surrogate(c); } + /// Returns true if c is lead code unit, always true of UTF-32 + static bool is_lead(char_type c) { return !is_second_surrogate(c); } + + template + static code_point decode(It& current, It last) + { + if(BOOST_UNLIKELY(current == last)) + return incomplete; + uint16_t w1 = *current++; + if(BOOST_LIKELY(w1 < 0xD800 || 0xDFFF < w1)) { + return w1; + } + if(w1 > 0xDBFF) + return illegal; + if(current == last) + return incomplete; + uint16_t w2 = *current++; + if(w2 < 0xDC00 || 0xDFFF < w2) + return illegal; + return combine_surrogate(w1, w2); + } + template + static code_point decode_valid(It& current) + { + uint16_t w1 = *current++; + if(BOOST_LIKELY(w1 < 0xD800 || 0xDFFF < w1)) { + return w1; + } + uint16_t w2 = *current++; + return combine_surrogate(w1, w2); + } + + static constexpr int max_width = 2; + static int width(code_point u) { return u >= 0x10000 ? 2 : 1; } + template + static It encode(code_point u, It out) + { + if(BOOST_LIKELY(u <= 0xFFFF)) { + *out++ = static_cast(u); + } else { + u -= 0x10000; + *out++ = static_cast(0xD800 | (u >> 10)); + *out++ = static_cast(0xDC00 | (u & 0x3FF)); + } + return out; + } + }; // utf16; + + template + struct utf_traits { + typedef CharType char_type; + static int trail_length(char_type c) + { + if(is_valid_codepoint(c)) + return 0; + return -1; + } + static bool is_trail(char_type /*c*/) { return false; } + static bool is_lead(char_type /*c*/) { return true; } + + template + static code_point decode_valid(It& current) + { + return *current++; + } + + template + static code_point decode(It& current, It last) + { + if(BOOST_UNLIKELY(current == last)) + return boost::locale::utf::incomplete; + code_point c = *current++; + if(BOOST_UNLIKELY(!is_valid_codepoint(c))) + return boost::locale::utf::illegal; + return c; + } + static constexpr int max_width = 1; + static int width(code_point /*u*/) { return 1; } + template + static It encode(code_point u, It out) + { + *out++ = static_cast(u); + return out; + } + + }; // utf32 + +#endif + + } // namespace utf +}} // namespace boost::locale + +#endif diff --git a/boost/locale/util/string.hpp b/boost/locale/util/string.hpp new file mode 100644 index 0000000..14e52e6 --- /dev/null +++ b/boost/locale/util/string.hpp @@ -0,0 +1,39 @@ +// +// Copyright (c) 2022-2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_UTIL_STRING_HPP +#define BOOST_LOCALE_UTIL_STRING_HPP + +#include + +namespace boost { namespace locale { namespace util { + /// Return the end of a C-string, i.e. the pointer to the trailing NULL byte + template + Char* str_end(Char* str) + { + while(*str) + ++str; + return str; + } + + inline bool is_upper_ascii(const char c) + { + return 'A' <= c && c <= 'Z'; + } + + inline bool is_lower_ascii(const char c) + { + return 'a' <= c && c <= 'z'; + } + + inline bool is_numeric_ascii(const char c) + { + return '0' <= c && c <= '9'; + } + +}}} // namespace boost::locale::util + +#endif \ No newline at end of file diff --git a/build.cmd b/build.cmd index 1eba561..b1dc8d3 100644 --- a/build.cmd +++ b/build.cmd @@ -7,6 +7,7 @@ bcp ^ boost/crc.hpp ^ boost/date_time/posix_time/posix_time.hpp ^ boost/icl/interval_map.hpp ^ + boost/locale/encoding_utf.hpp ^ boost/optional.hpp ^ boost/range/algorithm/ ^ boost/range/algorithm_ext/ ^ diff --git a/libs/atomic/build/Jamfile.v2 b/libs/atomic/build/Jamfile.v2 new file mode 100644 index 0000000..a795194 --- /dev/null +++ b/libs/atomic/build/Jamfile.v2 @@ -0,0 +1,147 @@ +# Boost.Atomic Library Jamfile +# +# Copyright Helge Bahmann 2011. +# Copyright Andrey Semashev 2018, 2020-2021. +# +# 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 path ; +import project ; +import feature ; +import configure ; +import atomic-arch-config ; + +local here = [ modules.binding $(__name__) ] ; + +project.push-current [ project.current ] ; +project.load [ path.join [ path.make $(here:D) ] ../../config/checks/architecture ] ; +project.pop-current ; + +lib synchronization ; +explicit synchronization ; + +rule check-synchronization-lib ( properties * ) +{ + local result ; + + if windows in $(properties) + { + if [ configure.builds ../config//has_synchronization_lib : $(properties) : "has synchronization.lib" ] + { + result += synchronization ; + } + else + { + result += BOOST_ATOMIC_NO_SYNCHRONIZATION_LIB ; + } + } + + return $(result) ; +} + +project boost/atomic + : requirements + ../src + multi + shared:BOOST_ATOMIC_DYN_LINK=1 + static:BOOST_ATOMIC_STATIC_LINK=1 + BOOST_ATOMIC_SOURCE + windows:BOOST_USE_WINDOWS_H + gcc,windows:"-lkernel32" + : usage-requirements + shared:BOOST_ATOMIC_DYN_LINK=1 + static:BOOST_ATOMIC_STATIC_LINK=1 + : source-location ../src + ; + +BOOST_ATOMIC_SOURCES_SSE2 = + find_address_sse2 +; + +BOOST_ATOMIC_SOURCES_SSE41 = + find_address_sse41 +; + +for local src in $(BOOST_ATOMIC_SOURCES_SSE2) +{ + obj $(src) + : ## sources ## + $(src).cpp + : ## requirements ## + @atomic-arch-config.sse2-flags + shared:BOOST_ATOMIC_DYN_LINK=1 + static:BOOST_ATOMIC_STATIC_LINK=1 + BOOST_ATOMIC_SOURCE + ; + + explicit $(src) ; +} + +for local src in $(BOOST_ATOMIC_SOURCES_SSE41) +{ + obj $(src) + : ## sources ## + $(src).cpp + : ## requirements ## + @atomic-arch-config.sse41-flags + shared:BOOST_ATOMIC_DYN_LINK=1 + static:BOOST_ATOMIC_STATIC_LINK=1 + BOOST_ATOMIC_SOURCE + ; + + explicit $(src) ; +} + +rule select-arch-specific-sources ( properties * ) +{ + local result ; + + if x86 in [ atomic-arch-config.deduce-architecture $(properties) ] + { + if [ configure.builds ../config//has_sse2 : $(properties) : "compiler supports SSE2" ] + { + result += $(BOOST_ATOMIC_SOURCES_SSE2) ; + result += BOOST_ATOMIC_USE_SSE2 ; + } + + if [ configure.builds ../config//has_sse41 : $(properties) : "compiler supports SSE4.1" ] + { + result += $(BOOST_ATOMIC_SOURCES_SSE41) ; + result += BOOST_ATOMIC_USE_SSE41 ; + } + } + +# ECHO "Arch sources: " $(result) ; + + return $(result) ; +} + +rule select-platform-specific-sources ( properties * ) +{ + local result ; + + if windows in $(properties) + { + result += wait_on_address.cpp ; + } + +# ECHO Platform sources: $(result) ; + + return $(result) ; +} + +lib boost_atomic + : ## sources ## + lock_pool.cpp + : ## requirements ## + ../src + @select-arch-specific-sources + @select-platform-specific-sources + : usage-requirements + @check-synchronization-lib + ; + +boost-install boost_atomic ; diff --git a/libs/atomic/build/atomic-arch-config.jam b/libs/atomic/build/atomic-arch-config.jam new file mode 100644 index 0000000..b31ab43 --- /dev/null +++ b/libs/atomic/build/atomic-arch-config.jam @@ -0,0 +1,127 @@ +# +# Copyright Andrey Semashev 2020. +# 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 jamfile contains utility rules to select architecture-specific compiler flags + +import common ; +import feature ; +import configure ; + +rule deduce-architecture ( properties * ) +{ + local architecture = [ feature.get-values "architecture" : $(properties) ] ; + if $(architecture) + { + return $(architecture) ; + } + else + { + if [ configure.builds /boost/architecture//x86 : $(properties) : "x86" ] + { + return x86 ; + } + else if [ configure.builds /boost/architecture//arm : $(properties) : "arm" ] + { + return arm ; + } + else if [ configure.builds /boost/architecture//mips1 : $(properties) : "mips1" ] + { + return mips1 ; + } + else if [ configure.builds /boost/architecture//power : $(properties) : "power" ] + { + return power ; + } + else if [ configure.builds /boost/architecture//sparc : $(properties) : "sparc" ] + { + return sparc ; + } + } +} + +rule deduce-address-model ( properties * ) +{ + local address_model = [ feature.get-values "address-model" : $(properties) ] ; + if $(address_model) + { + return $(address_model) ; + } + else + { + if [ configure.builds /boost/architecture//32 : $(properties) : "32-bit" ] + { + return 32 ; + } + else if [ configure.builds /boost/architecture//64 : $(properties) : "64-bit" ] + { + return 64 ; + } + } +} + +rule sse2-flags ( properties * ) +{ + local result ; + + if intel in $(properties) + { + if win in $(properties) + { + result = "/QxSSE2" ; + } + else + { + result = "-xSSE2" ; + } + } + else if 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 32 in [ deduce-address-model $(properties) ] + { + result = "/arch:SSE2" ; + } + } + else + { + result = "-msse -msse2" ; + } + + return $(result) ; +} + +rule sse41-flags ( properties * ) +{ + local result ; + + if intel in $(properties) + { + if win in $(properties) + { + result = "/QxSSE4.1" ; + } + else + { + result = "-xSSE4.1" ; + } + } + else if 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 32 in [ deduce-address-model $(properties) ] + { + result = "/arch:SSE2" ; + } + } + else + { + result = "-msse -msse2 -msse3 -mssse3 -msse4.1" ; + } + + return $(result) ; +} diff --git a/libs/atomic/config/Jamfile.v2 b/libs/atomic/config/Jamfile.v2 new file mode 100644 index 0000000..f7638f6 --- /dev/null +++ b/libs/atomic/config/Jamfile.v2 @@ -0,0 +1,20 @@ +# +# Copyright Andrey Semashev 2020. +# 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 atomic-arch-config ; + +lib synchronization ; +explicit synchronization ; + +exe has_synchronization_lib : has_synchronization_lib.cpp : synchronization ; +explicit has_synchronization_lib ; + +obj has_sse2 : has_sse2.cpp : @atomic-arch-config.sse2-flags ; +explicit has_sse2 ; + +obj has_sse41 : has_sse41.cpp : @atomic-arch-config.sse41-flags ; +explicit has_sse41 ; diff --git a/libs/atomic/config/has_sse2.cpp b/libs/atomic/config/has_sse2.cpp new file mode 100644 index 0000000..e99dc55 --- /dev/null +++ b/libs/atomic/config/has_sse2.cpp @@ -0,0 +1,17 @@ +/* + * Copyright Andrey Semashev 2020. + * 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 + +int main(int, char*[]) +{ + __m128i mm = _mm_setzero_si128(); + mm = _mm_cmpeq_epi32(mm, mm); + mm = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm), _mm_castsi128_ps(mm), _MM_SHUFFLE(2, 0, 2, 0))); + mm = _mm_packs_epi32(mm, mm); + return _mm_movemask_epi8(mm); +} diff --git a/libs/atomic/config/has_sse41.cpp b/libs/atomic/config/has_sse41.cpp new file mode 100644 index 0000000..218a27d --- /dev/null +++ b/libs/atomic/config/has_sse41.cpp @@ -0,0 +1,17 @@ +/* + * Copyright Andrey Semashev 2020. + * 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 + +int main(int, char*[]) +{ + __m128i mm = _mm_setzero_si128(); + mm = _mm_cmpeq_epi64(mm, mm); + mm = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm), _mm_castsi128_ps(mm), _MM_SHUFFLE(2, 0, 2, 0))); + mm = _mm_packs_epi32(mm, mm); + return _mm_movemask_epi8(mm); +} diff --git a/libs/atomic/config/has_synchronization_lib.cpp b/libs/atomic/config/has_synchronization_lib.cpp new file mode 100644 index 0000000..7ff97a6 --- /dev/null +++ b/libs/atomic/config/has_synchronization_lib.cpp @@ -0,0 +1,33 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ + +#define BOOST_USE_WINAPI_VERSION 0x0602 + +// Include Boost.Predef first so that windows.h is guaranteed to be not included +#include +#include +#if !BOOST_OS_WINDOWS && !BOOST_OS_CYGWIN +#error "This config test is for Windows only" +#endif + +#include +#include +#if !(BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN8 && (BOOST_WINAPI_PARTITION_APP || BOOST_WINAPI_PARTITION_SYSTEM)) +#error "No WaitOnAddress API" +#endif + +#include +#include +#include + +int main() +{ + unsigned int n = 0u, compare = 0u; + boost::winapi::WaitOnAddress(&n, &compare, sizeof(n), 0); + return 0; +} diff --git a/libs/atomic/src/bit_operation_tools.hpp b/libs/atomic/src/bit_operation_tools.hpp new file mode 100644 index 0000000..2c114b9 --- /dev/null +++ b/libs/atomic/src/bit_operation_tools.hpp @@ -0,0 +1,82 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file bit_operation_tools.hpp + * + * This file contains bit operation tools + */ + +#ifndef BOOST_ATOMIC_BIT_OPERATION_TOOLS_HPP_INCLUDED_ +#define BOOST_ATOMIC_BIT_OPERATION_TOOLS_HPP_INCLUDED_ + +#include + +#if BOOST_ARCH_X86 + +#include +#include + +#if defined(_MSC_VER) +extern "C" unsigned char _BitScanForward(unsigned long* index, unsigned long x); +#if defined(BOOST_MSVC) +#pragma intrinsic(_BitScanForward) +#endif +#endif + +namespace boost { +namespace atomics { +namespace detail { + +//! Counts trailing zero bits +BOOST_FORCEINLINE unsigned int count_trailing_zeros(unsigned int x) +{ +#if defined(__GNUC__) + return __builtin_ctz(x); +#elif defined(_MSC_VER) + unsigned long index; + _BitScanForward(&index, x); + return static_cast< unsigned int >(index); +#else + unsigned int index = 0u; + if ((x & 0xFFFF) == 0u) + { + x >>= 16; + index += 16u; + } + if ((x & 0xFF) == 0u) + { + x >>= 8; + index += 8u; + } + if ((x & 0xF) == 0u) + { + x >>= 4; + index += 4u; + } + if ((x & 0x3) == 0u) + { + x >>= 2; + index += 2u; + } + if ((x & 0x1) == 0u) + { + index += 1u; + } + return index; +#endif +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include + +#endif // BOOST_ARCH_X86 + +#endif // BOOST_ATOMIC_BIT_OPERATION_TOOLS_HPP_INCLUDED_ diff --git a/libs/atomic/src/cpuid.hpp b/libs/atomic/src/cpuid.hpp new file mode 100644 index 0000000..452917a --- /dev/null +++ b/libs/atomic/src/cpuid.hpp @@ -0,0 +1,86 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file cpuid.hpp + * + * This file contains declaration of \c cpuid function + */ + +#ifndef BOOST_ATOMIC_CPUID_HPP_INCLUDED_ +#define BOOST_ATOMIC_CPUID_HPP_INCLUDED_ + +#include + +#if BOOST_ARCH_X86 + +#if defined(_MSC_VER) +#include // __cpuid +#endif +#include +#include + +#include + +namespace boost { +namespace atomics { +namespace detail { + +//! The function invokes x86 cpuid instruction +inline 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.Atomic: Unsupported compiler, cpuid instruction cannot be generated" +#endif +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include + +#endif // BOOST_ARCH_X86 + +#endif // BOOST_ATOMIC_CPUID_HPP_INCLUDED_ diff --git a/libs/atomic/src/find_address.hpp b/libs/atomic/src/find_address.hpp new file mode 100644 index 0000000..c841c68 --- /dev/null +++ b/libs/atomic/src/find_address.hpp @@ -0,0 +1,45 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file find_address.hpp + * + * This file contains declaration of \c find_address algorithm + */ + +#ifndef BOOST_ATOMIC_FIND_ADDRESS_HPP_INCLUDED_ +#define BOOST_ATOMIC_FIND_ADDRESS_HPP_INCLUDED_ + +#include +#include +#include +#include +#include + +namespace boost { +namespace atomics { +namespace detail { + +//! \c find_address signature +typedef std::size_t (find_address_t)(const volatile void* addr, const volatile void* const* addrs, std::size_t size); + +extern find_address_t find_address_generic; + +#if BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) +extern find_address_t find_address_sse2; +#if BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 +extern find_address_t find_address_sse41; +#endif // BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 +#endif // BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include + +#endif // BOOST_ATOMIC_FIND_ADDRESS_HPP_INCLUDED_ diff --git a/libs/atomic/src/find_address_sse2.cpp b/libs/atomic/src/find_address_sse2.cpp new file mode 100644 index 0000000..3e624dd --- /dev/null +++ b/libs/atomic/src/find_address_sse2.cpp @@ -0,0 +1,289 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file find_address_sse2.cpp + * + * This file contains SSE2 implementation of the \c find_address algorithm + */ + +#include +#include + +#if BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) + +#include +#include + +#include +#include +#include +#include "find_address.hpp" +#include "x86_vector_tools.hpp" +#include "bit_operation_tools.hpp" + +#include + +namespace boost { +namespace atomics { +namespace detail { + +#if BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 +namespace { + +BOOST_FORCEINLINE __m128i mm_pand_si128(__m128i mm1, __m128i mm2) +{ + // As of 2020, gcc, clang and icc prefer to generate andps instead of pand if the surrounding + // instructions pertain to FP domain, even if we use the _mm_and_si128 intrinsic. In our + // algorithm implementation, the FP instruction happen to be shufps, which is not actually + // restricted to FP domain (it is actually implemented in a separate MMX EU in Pentium 4 or + // a shuffle EU in INT domain in Core 2; on AMD K8/K10 all SSE instructions are implemented in + // FADD, FMUL and FMISC EUs regardless of INT/FP data types, and shufps is implemented in FADD/FMUL). + // In other words, there should be no domain bypass penalty between shufps and pand. + // + // This would usually not pose a problem since andps and pand have the same latency and throughput + // on most architectures of that age (before SSE4.1). However, it is possible that a newer architecture + // runs the SSE2 code path (e.g. because some weird compiler doesn't support SSE4.1 or because + // a hypervisor blocks SSE4.1 detection), and there pand may have a better throughput. For example, + // Sandy Bridge can execute 3 pand instructions per cycle, but only one andps. For this reason + // we prefer to generate pand and not andps. +#if defined(__GNUC__) +#if defined(__AVX__) + // Generate VEX-coded variant if the code is compiled for AVX and later. + __asm__("vpand %1, %0, %0\n\t" : "+x" (mm1) : "x" (mm2)); +#else + __asm__("pand %1, %0\n\t" : "+x" (mm1) : "x" (mm2)); +#endif +#else + mm1 = _mm_and_si128(mm1, mm2); +#endif + return mm1; +} + +} // namespace +#endif // BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 + +//! SSE2 implementation of the \c find_address algorithm +std::size_t find_address_sse2(const volatile void* addr, const volatile void* const* addrs, std::size_t size) +{ +#if BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 + + if (size < 12u) + return find_address_generic(addr, addrs, size); + + const __m128i mm_addr = mm_set1_epiptr((uintptr_t)addr); + std::size_t pos = 0u; + const std::size_t n = (size + 1u) & ~static_cast< std::size_t >(1u); + for (std::size_t m = n & ~static_cast< std::size_t >(15u); pos < m; pos += 16u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 2u)); + __m128i mm3 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 4u)); + __m128i mm4 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 6u)); + __m128i mm5 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 8u)); + __m128i mm6 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 10u)); + __m128i mm7 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 12u)); + __m128i mm8 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 14u)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + mm2 = _mm_cmpeq_epi32(mm2, mm_addr); + mm3 = _mm_cmpeq_epi32(mm3, mm_addr); + mm4 = _mm_cmpeq_epi32(mm4, mm_addr); + mm5 = _mm_cmpeq_epi32(mm5, mm_addr); + mm6 = _mm_cmpeq_epi32(mm6, mm_addr); + mm7 = _mm_cmpeq_epi32(mm7, mm_addr); + mm8 = _mm_cmpeq_epi32(mm8, mm_addr); + + __m128i mm_mask1_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask1_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(3, 1, 3, 1))); + + __m128i mm_mask2_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm3), _mm_castsi128_ps(mm4), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask2_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm3), _mm_castsi128_ps(mm4), _MM_SHUFFLE(3, 1, 3, 1))); + + __m128i mm_mask3_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm5), _mm_castsi128_ps(mm6), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask3_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm5), _mm_castsi128_ps(mm6), _MM_SHUFFLE(3, 1, 3, 1))); + + __m128i mm_mask4_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm7), _mm_castsi128_ps(mm8), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask4_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm7), _mm_castsi128_ps(mm8), _MM_SHUFFLE(3, 1, 3, 1))); + + mm_mask1_lo = mm_pand_si128(mm_mask1_lo, mm_mask1_hi); + mm_mask2_lo = mm_pand_si128(mm_mask2_lo, mm_mask2_hi); + mm_mask3_lo = mm_pand_si128(mm_mask3_lo, mm_mask3_hi); + mm_mask4_lo = mm_pand_si128(mm_mask4_lo, mm_mask4_hi); + + mm_mask1_lo = _mm_packs_epi32(mm_mask1_lo, mm_mask2_lo); + mm_mask3_lo = _mm_packs_epi32(mm_mask3_lo, mm_mask4_lo); + + mm_mask1_lo = _mm_packs_epi16(mm_mask1_lo, mm_mask3_lo); + + uint32_t mask = _mm_movemask_epi8(mm_mask1_lo); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + } + + if ((n - pos) >= 8u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 2u)); + __m128i mm3 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 4u)); + __m128i mm4 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 6u)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + mm2 = _mm_cmpeq_epi32(mm2, mm_addr); + mm3 = _mm_cmpeq_epi32(mm3, mm_addr); + mm4 = _mm_cmpeq_epi32(mm4, mm_addr); + + __m128i mm_mask1_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask1_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(3, 1, 3, 1))); + + __m128i mm_mask2_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm3), _mm_castsi128_ps(mm4), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask2_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm3), _mm_castsi128_ps(mm4), _MM_SHUFFLE(3, 1, 3, 1))); + + mm_mask1_lo = mm_pand_si128(mm_mask1_lo, mm_mask1_hi); + mm_mask2_lo = mm_pand_si128(mm_mask2_lo, mm_mask2_hi); + + mm_mask1_lo = _mm_packs_epi32(mm_mask1_lo, mm_mask2_lo); + + uint32_t mask = _mm_movemask_epi8(mm_mask1_lo); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask) / 2u; + goto done; + } + + pos += 8u; + } + + if ((n - pos) >= 4u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 2u)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + mm2 = _mm_cmpeq_epi32(mm2, mm_addr); + + __m128i mm_mask1_lo = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(2, 0, 2, 0))); + __m128i mm_mask1_hi = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(3, 1, 3, 1))); + + mm_mask1_lo = mm_pand_si128(mm_mask1_lo, mm_mask1_hi); + + uint32_t mask = _mm_movemask_ps(_mm_castsi128_ps(mm_mask1_lo)); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + + pos += 4u; + } + + if (pos < n) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + __m128i mm_mask = _mm_shuffle_epi32(mm1, _MM_SHUFFLE(2, 3, 0, 1)); + mm_mask = mm_pand_si128(mm_mask, mm1); + + uint32_t mask = _mm_movemask_pd(_mm_castsi128_pd(mm_mask)); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + + pos += 2u; + } + +done: + return pos; + +#else // BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 + + if (size < 10u) + return find_address_generic(addr, addrs, size); + + const __m128i mm_addr = _mm_set1_epi32((uintptr_t)addr); + std::size_t pos = 0u; + const std::size_t n = (size + 3u) & ~static_cast< std::size_t >(3u); + for (std::size_t m = n & ~static_cast< std::size_t >(15u); pos < m; pos += 16u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 4u)); + __m128i mm3 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 8u)); + __m128i mm4 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 12u)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + mm2 = _mm_cmpeq_epi32(mm2, mm_addr); + mm3 = _mm_cmpeq_epi32(mm3, mm_addr); + mm4 = _mm_cmpeq_epi32(mm4, mm_addr); + + mm1 = _mm_packs_epi32(mm1, mm2); + mm3 = _mm_packs_epi32(mm3, mm4); + + mm1 = _mm_packs_epi16(mm1, mm3); + + uint32_t mask = _mm_movemask_epi8(mm1); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + } + + if ((n - pos) >= 8u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 4u)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + mm2 = _mm_cmpeq_epi32(mm2, mm_addr); + + mm1 = _mm_packs_epi32(mm1, mm2); + + uint32_t mask = _mm_movemask_epi8(mm1); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask) / 2u; + goto done; + } + + pos += 8u; + } + + if (pos < n) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + + mm1 = _mm_cmpeq_epi32(mm1, mm_addr); + + uint32_t mask = _mm_movemask_ps(_mm_castsi128_ps(mm1)); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + + pos += 4u; + } + +done: + return pos; + +#endif // BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include + +#endif // BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) diff --git a/libs/atomic/src/find_address_sse41.cpp b/libs/atomic/src/find_address_sse41.cpp new file mode 100644 index 0000000..1db1b74 --- /dev/null +++ b/libs/atomic/src/find_address_sse41.cpp @@ -0,0 +1,154 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file find_address_sse41.cpp + * + * This file contains SSE4.1 implementation of the \c find_address algorithm + */ + +#include +#include + +#if BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8) + +#include +#include + +#include +#include +#include +#include "find_address.hpp" +#include "x86_vector_tools.hpp" +#include "bit_operation_tools.hpp" + +#include + +namespace boost { +namespace atomics { +namespace detail { + +//! SSE4.1 implementation of the \c find_address algorithm +std::size_t find_address_sse41(const volatile void* addr, const volatile void* const* addrs, std::size_t size) +{ + if (size < 12u) + return find_address_generic(addr, addrs, size); + + const __m128i mm_addr = mm_set1_epiptr((uintptr_t)addr); + std::size_t pos = 0u; + const std::size_t n = (size + 1u) & ~static_cast< std::size_t >(1u); + for (std::size_t m = n & ~static_cast< std::size_t >(15u); pos < m; pos += 16u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 2u)); + __m128i mm3 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 4u)); + __m128i mm4 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 6u)); + __m128i mm5 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 8u)); + __m128i mm6 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 10u)); + __m128i mm7 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 12u)); + __m128i mm8 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 14u)); + + mm1 = _mm_cmpeq_epi64(mm1, mm_addr); + mm2 = _mm_cmpeq_epi64(mm2, mm_addr); + mm3 = _mm_cmpeq_epi64(mm3, mm_addr); + mm4 = _mm_cmpeq_epi64(mm4, mm_addr); + mm5 = _mm_cmpeq_epi64(mm5, mm_addr); + mm6 = _mm_cmpeq_epi64(mm6, mm_addr); + mm7 = _mm_cmpeq_epi64(mm7, mm_addr); + mm8 = _mm_cmpeq_epi64(mm8, mm_addr); + + mm1 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(2, 0, 2, 0))); + mm3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm3), _mm_castsi128_ps(mm4), _MM_SHUFFLE(2, 0, 2, 0))); + mm5 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm5), _mm_castsi128_ps(mm6), _MM_SHUFFLE(2, 0, 2, 0))); + mm7 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm7), _mm_castsi128_ps(mm8), _MM_SHUFFLE(2, 0, 2, 0))); + + mm1 = _mm_packs_epi32(mm1, mm3); + mm5 = _mm_packs_epi32(mm5, mm7); + + mm1 = _mm_packs_epi16(mm1, mm5); + + uint32_t mask = _mm_movemask_epi8(mm1); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + } + + if ((n - pos) >= 8u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 2u)); + __m128i mm3 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 4u)); + __m128i mm4 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 6u)); + + mm1 = _mm_cmpeq_epi64(mm1, mm_addr); + mm2 = _mm_cmpeq_epi64(mm2, mm_addr); + mm3 = _mm_cmpeq_epi64(mm3, mm_addr); + mm4 = _mm_cmpeq_epi64(mm4, mm_addr); + + mm1 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(2, 0, 2, 0))); + mm3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm3), _mm_castsi128_ps(mm4), _MM_SHUFFLE(2, 0, 2, 0))); + + mm1 = _mm_packs_epi32(mm1, mm3); + + uint32_t mask = _mm_movemask_epi8(mm1); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask) / 2u; + goto done; + } + + pos += 8u; + } + + if ((n - pos) >= 4u) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + __m128i mm2 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos + 2u)); + + mm1 = _mm_cmpeq_epi64(mm1, mm_addr); + mm2 = _mm_cmpeq_epi64(mm2, mm_addr); + + mm1 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(mm1), _mm_castsi128_ps(mm2), _MM_SHUFFLE(2, 0, 2, 0))); + + uint32_t mask = _mm_movemask_ps(_mm_castsi128_ps(mm1)); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + + pos += 4u; + } + + if (pos < n) + { + __m128i mm1 = _mm_load_si128(reinterpret_cast< const __m128i* >(addrs + pos)); + + mm1 = _mm_cmpeq_epi64(mm1, mm_addr); + uint32_t mask = _mm_movemask_pd(_mm_castsi128_pd(mm1)); + if (mask) + { + pos += atomics::detail::count_trailing_zeros(mask); + goto done; + } + + pos += 2u; + } + +done: + return pos; +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include + +#endif // BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8) diff --git a/libs/atomic/src/lock_pool.cpp b/libs/atomic/src/lock_pool.cpp new file mode 100644 index 0000000..7424338 --- /dev/null +++ b/libs/atomic/src/lock_pool.cpp @@ -0,0 +1,1414 @@ +/* + * 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) + * + * Copyright (c) 2011 Helge Bahmann + * Copyright (c) 2013-2014, 2020 Andrey Semashev + */ +/*! + * \file lock_pool.cpp + * + * This file contains implementation of the lock pool used to emulate atomic ops. + */ + +#include +#if BOOST_OS_WINDOWS +// Include boost/winapi/config.hpp first to make sure target Windows version is selected by Boost.WinAPI +#include +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if BOOST_OS_WINDOWS +#include +#include +#include +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 +#include +#include +#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 +#include +#include +#include +#include +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 +#define BOOST_ATOMIC_USE_WINAPI +#else // BOOST_OS_WINDOWS +#include +#if defined(BOOST_ATOMIC_DETAIL_HAS_FUTEX) && BOOST_ATOMIC_INT32_LOCK_FREE == 2 +#define BOOST_ATOMIC_USE_FUTEX +#else // BOOST_OS_LINUX +#include +#define BOOST_ATOMIC_USE_PTHREAD +#endif // BOOST_OS_LINUX +#include +#endif // BOOST_OS_WINDOWS + +#include "find_address.hpp" + +#if BOOST_ARCH_X86 && (defined(BOOST_ATOMIC_USE_SSE2) || defined(BOOST_ATOMIC_USE_SSE41)) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && \ + (\ + (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 && BOOST_HW_SIMD_X86 < BOOST_HW_SIMD_X86_SSE4_1_VERSION) || \ + (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4 && BOOST_HW_SIMD_X86 < BOOST_HW_SIMD_X86_SSE2_VERSION) \ + ) +#include "cpuid.hpp" +#define BOOST_ATOMIC_DETAIL_X86_USE_RUNTIME_DISPATCH +#endif + +#include + +// Cache line size, in bytes +// NOTE: This constant is made as a macro because some compilers (gcc 4.4 for one) don't allow enums or namespace scope constants in alignment attributes +#if defined(__s390__) || defined(__s390x__) +#define BOOST_ATOMIC_CACHE_LINE_SIZE 256 +#elif defined(powerpc) || defined(__powerpc__) || defined(__ppc__) +#define BOOST_ATOMIC_CACHE_LINE_SIZE 128 +#else +#define BOOST_ATOMIC_CACHE_LINE_SIZE 64 +#endif + +namespace boost { +namespace atomics { +namespace detail { + +//! \c find_address generic implementation +std::size_t find_address_generic(const volatile void* addr, const volatile void* const* addrs, std::size_t size) +{ + for (std::size_t i = 0u; i < size; ++i) + { + if (addrs[i] == addr) + return i; + } + + return size; +} + +namespace lock_pool { + +namespace { + +#if BOOST_ARCH_X86 && (defined(BOOST_ATOMIC_USE_SSE2) || defined(BOOST_ATOMIC_USE_SSE41)) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) + +typedef atomics::detail::core_operations< sizeof(find_address_t*), false, false > func_ptr_operations; +BOOST_STATIC_ASSERT_MSG(func_ptr_operations::is_always_lock_free, "Boost.Atomic unsupported target platform: native atomic operations not implemented for function pointers"); + +#if defined(BOOST_ATOMIC_DETAIL_X86_USE_RUNTIME_DISPATCH) +std::size_t find_address_dispatch(const volatile void* addr, const volatile void* const* addrs, std::size_t size); +#endif + +union find_address_ptr +{ + find_address_t* as_ptr; + func_ptr_operations::storage_type as_storage; +} +g_find_address = +{ +#if defined(BOOST_ATOMIC_USE_SSE41) && BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 && BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_SSE4_1_VERSION + &find_address_sse41 +#elif defined(BOOST_ATOMIC_USE_SSE2) && BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4 && BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_SSE2_VERSION + &find_address_sse2 +#else + &find_address_dispatch +#endif +}; + +#if defined(BOOST_ATOMIC_DETAIL_X86_USE_RUNTIME_DISPATCH) + +std::size_t find_address_dispatch(const volatile void* addr, const volatile void* const* addrs, std::size_t size) +{ + find_address_t* find_addr = &find_address_generic; + +#if defined(BOOST_ATOMIC_USE_SSE2) + // First, check the max available cpuid function + uint32_t eax = 0u, ebx = 0u, ecx = 0u, edx = 0u; + atomics::detail::cpuid(eax, ebx, ecx, edx); + + const uint32_t max_cpuid_function = eax; + if (max_cpuid_function >= 1u) + { + // Obtain CPU features + eax = 1u; + ebx = ecx = edx = 0u; + atomics::detail::cpuid(eax, ebx, ecx, edx); + + if ((edx & (1u << 26)) != 0u) + find_addr = &find_address_sse2; + +#if defined(BOOST_ATOMIC_USE_SSE41) && BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 + if ((ecx & (1u << 19)) != 0u) + find_addr = &find_address_sse41; +#endif + } +#endif // defined(BOOST_ATOMIC_USE_SSE2) + + find_address_ptr ptr = {}; + ptr.as_ptr = find_addr; + func_ptr_operations::store(g_find_address.as_storage, ptr.as_storage, boost::memory_order_relaxed); + + return find_addr(addr, addrs, size); +} + +#endif // defined(BOOST_ATOMIC_DETAIL_X86_USE_RUNTIME_DISPATCH) + +inline std::size_t find_address(const volatile void* addr, const volatile void* const* addrs, std::size_t size) +{ + find_address_ptr ptr; + ptr.as_storage = func_ptr_operations::load(g_find_address.as_storage, boost::memory_order_relaxed); + return ptr.as_ptr(addr, addrs, size); +} + +#else // BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) + +inline std::size_t find_address(const volatile void* addr, const volatile void* const* addrs, std::size_t size) +{ + return atomics::detail::find_address_generic(addr, addrs, size); +} + +#endif // BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8 || BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 4) + +struct wait_state; +struct lock_state; + +//! Base class for a wait state +struct wait_state_base +{ + //! Number of waiters referencing this state + std::size_t m_ref_count; + //! Index of this wait state in the list + std::size_t m_index; + + explicit wait_state_base(std::size_t index) BOOST_NOEXCEPT : + m_ref_count(0u), + m_index(index) + { + } + + BOOST_DELETED_FUNCTION(wait_state_base(wait_state_base const&)) + BOOST_DELETED_FUNCTION(wait_state_base& operator= (wait_state_base const&)) +}; + +//! List of wait states. Must be a POD structure. +struct wait_state_list +{ + //! List header + struct header + { + //! List size + std::size_t size; + //! List capacity + std::size_t capacity; + }; + + /*! + * \brief Pointer to the list header + * + * The list buffer consists of three adjacent areas: header object, array of atomic pointers and array of pointers to the wait_state structures. + * Each of the arrays have header.capacity elements, of which the first header.size elements correspond to the currently ongoing wait operations + * and the rest are spare elements. Spare wait_state structures may still be allocated (in which case the wait_state pointer is not null) and + * can be reused on future requests. Spare atomic pointers are null and unused. + * + * This memory layout was designed to optimize wait state lookup by atomic address and also support memory pooling to reduce dynamic memory allocations. + */ + header* m_header; + //! The flag indicates that memory pooling is disabled. Set on process cleanup. + bool m_free_memory; + + //! Buffer alignment, in bytes + static BOOST_CONSTEXPR_OR_CONST std::size_t buffer_alignment = 16u; + //! Alignment of pointer arrays in the buffer, in bytes. This should align atomic pointers to the vector size used in \c find_address implementation. + static BOOST_CONSTEXPR_OR_CONST std::size_t entries_alignment = atomics::detail::alignment_of< void* >::value < 16u ? 16u : atomics::detail::alignment_of< void* >::value; + //! Offset from the list header to the beginning of the array of atomic pointers in the buffer, in bytes + static BOOST_CONSTEXPR_OR_CONST std::size_t entries_offset = (sizeof(header) + entries_alignment - 1u) & ~static_cast< std::size_t >(entries_alignment - 1u); + //! Initial buffer capacity, in elements. This should be at least as large as a vector size used in \c find_address implementation. + static BOOST_CONSTEXPR_OR_CONST std::size_t initial_capacity = (16u / sizeof(void*)) < 2u ? 2u : (16u / sizeof(void*)); + + //! Returns a pointer to the array of atomic pointers + static const volatile void** get_atomic_pointers(header* p) BOOST_NOEXCEPT + { + BOOST_ASSERT(p != NULL); + return reinterpret_cast< const volatile void** >(reinterpret_cast< unsigned char* >(p) + entries_offset); + } + + //! Returns a pointer to the array of atomic pointers + const volatile void** get_atomic_pointers() const BOOST_NOEXCEPT + { + return get_atomic_pointers(m_header); + } + + //! Returns a pointer to the array of pointers to the wait states + static wait_state** get_wait_states(const volatile void** ptrs, std::size_t capacity) BOOST_NOEXCEPT + { + return reinterpret_cast< wait_state** >(const_cast< void** >(ptrs + capacity)); + } + + //! Returns a pointer to the array of pointers to the wait states + static wait_state** get_wait_states(header* p) BOOST_NOEXCEPT + { + return get_wait_states(get_atomic_pointers(p), p->capacity); + } + + //! Returns a pointer to the array of pointers to the wait states + wait_state** get_wait_states() const BOOST_NOEXCEPT + { + return get_wait_states(m_header); + } + + //! Finds an element with the given pointer to the atomic object + wait_state* find(const volatile void* addr) const BOOST_NOEXCEPT + { + wait_state* ws = NULL; + if (BOOST_LIKELY(m_header != NULL)) + { + const volatile void* const* addrs = get_atomic_pointers(); + const std::size_t size = m_header->size; + std::size_t pos = find_address(addr, addrs, size); + if (pos < size) + ws = get_wait_states()[pos]; + } + + return ws; + } + + //! Finds an existing element with the given pointer to the atomic object or allocates a new one. Returns NULL in case of failure. + wait_state* find_or_create(const volatile void* addr) BOOST_NOEXCEPT; + //! Releases the previously created wait state + void erase(wait_state* w) BOOST_NOEXCEPT; + + //! Deallocates spare entries and the list buffer if no allocated entries are left + void free_spare() BOOST_NOEXCEPT; + //! Allocates new buffer for the list entries. Returns NULL in case of failure. + static header* allocate_buffer(std::size_t new_capacity, header* old_header = NULL) BOOST_NOEXCEPT; +}; + +#define BOOST_ATOMIC_WAIT_STATE_LIST_INIT { NULL, false } + +// In the platform-specific definitions below, lock_state must be a POD structure and wait_state must derive from wait_state_base. + +#if defined(BOOST_ATOMIC_USE_PTHREAD) + +//! State of a wait operation associated with an atomic object +struct wait_state : + public wait_state_base +{ + //! Condition variable + pthread_cond_t m_cond; + + explicit wait_state(std::size_t index) BOOST_NOEXCEPT : + wait_state_base(index) + { + BOOST_VERIFY(pthread_cond_init(&m_cond, NULL) == 0); + } + + ~wait_state() BOOST_NOEXCEPT + { + pthread_cond_destroy(&m_cond); + } + + //! Blocks in the wait operation until notified + void wait(lock_state& state) BOOST_NOEXCEPT; + + //! Wakes up one thread blocked in the wait operation + void notify_one(lock_state&) BOOST_NOEXCEPT + { + BOOST_VERIFY(pthread_cond_signal(&m_cond) == 0); + } + //! Wakes up all threads blocked in the wait operation + void notify_all(lock_state&) BOOST_NOEXCEPT + { + BOOST_VERIFY(pthread_cond_broadcast(&m_cond) == 0); + } +}; + +//! Lock pool entry +struct lock_state +{ + //! Mutex + pthread_mutex_t m_mutex; + //! Wait states + wait_state_list m_wait_states; + + //! Locks the mutex for a short duration + void short_lock() BOOST_NOEXCEPT + { + long_lock(); + } + + //! Locks the mutex for a long duration + void long_lock() BOOST_NOEXCEPT + { + for (unsigned int i = 0u; i < 5u; ++i) + { + if (BOOST_LIKELY(pthread_mutex_trylock(&m_mutex) == 0)) + return; + + atomics::detail::pause(); + } + + BOOST_VERIFY(pthread_mutex_lock(&m_mutex) == 0); + } + + //! Unlocks the mutex + void unlock() BOOST_NOEXCEPT + { + BOOST_VERIFY(pthread_mutex_unlock(&m_mutex) == 0); + } +}; + +#define BOOST_ATOMIC_LOCK_STATE_INIT { PTHREAD_MUTEX_INITIALIZER, BOOST_ATOMIC_WAIT_STATE_LIST_INIT } + +//! Blocks in the wait operation until notified +inline void wait_state::wait(lock_state& state) BOOST_NOEXCEPT +{ + BOOST_VERIFY(pthread_cond_wait(&m_cond, &state.m_mutex) == 0); +} + +#elif defined(BOOST_ATOMIC_USE_FUTEX) + +typedef atomics::detail::core_operations< 4u, false, false > futex_operations; +// The storage type must be a 32-bit object, as required by futex API +BOOST_STATIC_ASSERT_MSG(futex_operations::is_always_lock_free && sizeof(futex_operations::storage_type) == 4u, "Boost.Atomic unsupported target platform: native atomic operations not implemented for 32-bit integers"); +typedef atomics::detail::extra_operations< futex_operations, futex_operations::storage_size, futex_operations::is_signed > futex_extra_operations; + +namespace mutex_bits { + +//! The bit indicates a locked mutex +BOOST_CONSTEXPR_OR_CONST futex_operations::storage_type locked = 1u; +//! The bit indicates that there is at least one thread blocked waiting for the mutex to be released +BOOST_CONSTEXPR_OR_CONST futex_operations::storage_type contended = 1u << 1; +//! The lowest bit of the counter bits used to mitigate ABA problem. This and any higher bits in the mutex state constitute the counter. +BOOST_CONSTEXPR_OR_CONST futex_operations::storage_type counter_one = 1u << 2; + +} // namespace mutex_bits + +//! State of a wait operation associated with an atomic object +struct wait_state : + public wait_state_base +{ + //! Condition variable futex. Used as the counter of notify calls. + BOOST_ATOMIC_DETAIL_ALIGNED_VAR(futex_operations::storage_alignment, futex_operations::storage_type, m_cond); + //! Number of currently blocked waiters + futex_operations::storage_type m_waiter_count; + + explicit wait_state(std::size_t index) BOOST_NOEXCEPT : + wait_state_base(index), + m_cond(0u), + m_waiter_count(0u) + { + } + + //! Blocks in the wait operation until notified + void wait(lock_state& state) BOOST_NOEXCEPT; + + //! Wakes up one thread blocked in the wait operation + void notify_one(lock_state& state) BOOST_NOEXCEPT; + //! Wakes up all threads blocked in the wait operation + void notify_all(lock_state& state) BOOST_NOEXCEPT; +}; + +//! Lock pool entry +struct lock_state +{ + //! Mutex futex + BOOST_ATOMIC_DETAIL_ALIGNED_VAR(futex_operations::storage_alignment, futex_operations::storage_type, m_mutex); + //! Wait states + wait_state_list m_wait_states; + + //! Locks the mutex for a short duration + void short_lock() BOOST_NOEXCEPT + { + long_lock(); + } + + //! Locks the mutex for a long duration + void long_lock() BOOST_NOEXCEPT + { + for (unsigned int i = 0u; i < 10u; ++i) + { + futex_operations::storage_type prev_state = futex_operations::load(m_mutex, boost::memory_order_relaxed); + if (BOOST_LIKELY((prev_state & mutex_bits::locked) == 0u)) + { + futex_operations::storage_type new_state = prev_state | mutex_bits::locked; + if (BOOST_LIKELY(futex_operations::compare_exchange_strong(m_mutex, prev_state, new_state, boost::memory_order_acquire, boost::memory_order_relaxed))) + return; + } + + atomics::detail::pause(); + } + + lock_slow_path(); + } + + //! Locks the mutex for a long duration + void lock_slow_path() BOOST_NOEXCEPT + { + futex_operations::storage_type prev_state = futex_operations::load(m_mutex, boost::memory_order_relaxed); + while (true) + { + if (BOOST_LIKELY((prev_state & mutex_bits::locked) == 0u)) + { + futex_operations::storage_type new_state = prev_state | mutex_bits::locked; + if (BOOST_LIKELY(futex_operations::compare_exchange_weak(m_mutex, prev_state, new_state, boost::memory_order_acquire, boost::memory_order_relaxed))) + return; + } + else + { + futex_operations::storage_type new_state = prev_state | mutex_bits::contended; + if (BOOST_LIKELY(futex_operations::compare_exchange_weak(m_mutex, prev_state, new_state, boost::memory_order_relaxed, boost::memory_order_relaxed))) + { + atomics::detail::futex_wait_private(&m_mutex, new_state); + prev_state = futex_operations::load(m_mutex, boost::memory_order_relaxed); + } + } + } + } + + //! Unlocks the mutex + void unlock() BOOST_NOEXCEPT + { + futex_operations::storage_type prev_state = futex_operations::load(m_mutex, boost::memory_order_relaxed); + futex_operations::storage_type new_state; + while (true) + { + new_state = (prev_state & (~mutex_bits::locked)) + mutex_bits::counter_one; + if (BOOST_LIKELY(futex_operations::compare_exchange_weak(m_mutex, prev_state, new_state, boost::memory_order_release, boost::memory_order_relaxed))) + break; + } + + if ((prev_state & mutex_bits::contended) != 0u) + { + int woken_count = atomics::detail::futex_signal_private(&m_mutex); + if (woken_count == 0) + { + prev_state = new_state; + new_state &= ~mutex_bits::contended; + futex_operations::compare_exchange_strong(m_mutex, prev_state, new_state, boost::memory_order_relaxed, boost::memory_order_relaxed); + } + } + } +}; + +#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_ALIGNAS) +#define BOOST_ATOMIC_LOCK_STATE_INIT { 0u, BOOST_ATOMIC_WAIT_STATE_LIST_INIT } +#else +#define BOOST_ATOMIC_LOCK_STATE_INIT { { 0u }, BOOST_ATOMIC_WAIT_STATE_LIST_INIT } +#endif + +//! Blocks in the wait operation until notified +inline void wait_state::wait(lock_state& state) BOOST_NOEXCEPT +{ + const futex_operations::storage_type prev_cond = m_cond; + ++m_waiter_count; + + state.unlock(); + + while (true) + { + int err = atomics::detail::futex_wait_private(&m_cond, prev_cond); + if (BOOST_LIKELY(err != EINTR)) + break; + } + + state.long_lock(); + + --m_waiter_count; +} + +//! Wakes up one thread blocked in the wait operation +inline void wait_state::notify_one(lock_state& state) BOOST_NOEXCEPT +{ + ++m_cond; + + if (BOOST_LIKELY(m_waiter_count > 0u)) + { + // Move one blocked thread to the mutex futex and mark the mutex contended so that the thread is unblocked on unlock() + atomics::detail::futex_requeue_private(&m_cond, &state.m_mutex, 0u, 1u); + futex_extra_operations::opaque_or(state.m_mutex, mutex_bits::contended, boost::memory_order_relaxed); + } +} + +//! Wakes up all threads blocked in the wait operation +inline void wait_state::notify_all(lock_state& state) BOOST_NOEXCEPT +{ + ++m_cond; + + if (BOOST_LIKELY(m_waiter_count > 0u)) + { + // Move blocked threads to the mutex futex and mark the mutex contended so that a thread is unblocked on unlock() + atomics::detail::futex_requeue_private(&m_cond, &state.m_mutex, 0u); + futex_extra_operations::opaque_or(state.m_mutex, mutex_bits::contended, boost::memory_order_relaxed); + } +} + +#else + +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +//! State of a wait operation associated with an atomic object +struct wait_state : + public wait_state_base +{ + //! Condition variable + boost::winapi::CONDITION_VARIABLE_ m_cond; + + explicit wait_state(std::size_t index) BOOST_NOEXCEPT : + wait_state_base(index) + { + boost::winapi::InitializeConditionVariable(&m_cond); + } + + //! Blocks in the wait operation until notified + void wait(lock_state& state) BOOST_NOEXCEPT; + + //! Wakes up one thread blocked in the wait operation + void notify_one(lock_state&) BOOST_NOEXCEPT + { + boost::winapi::WakeConditionVariable(&m_cond); + } + //! Wakes up all threads blocked in the wait operation + void notify_all(lock_state&) BOOST_NOEXCEPT + { + boost::winapi::WakeAllConditionVariable(&m_cond); + } +}; + +//! Lock pool entry +struct lock_state +{ + //! Mutex + boost::winapi::SRWLOCK_ m_mutex; + //! Wait states + wait_state_list m_wait_states; + + //! Locks the mutex for a short duration + void short_lock() BOOST_NOEXCEPT + { + long_lock(); + } + + //! Locks the mutex for a long duration + void long_lock() BOOST_NOEXCEPT + { + // Presumably, AcquireSRWLockExclusive already implements spinning internally, so there's no point in doing this ourselves. + boost::winapi::AcquireSRWLockExclusive(&m_mutex); + } + + //! Unlocks the mutex + void unlock() BOOST_NOEXCEPT + { + boost::winapi::ReleaseSRWLockExclusive(&m_mutex); + } +}; + +#define BOOST_ATOMIC_LOCK_STATE_INIT { BOOST_WINAPI_SRWLOCK_INIT, BOOST_ATOMIC_WAIT_STATE_LIST_INIT } + +//! Blocks in the wait operation until notified +inline void wait_state::wait(lock_state& state) BOOST_NOEXCEPT +{ + boost::winapi::SleepConditionVariableSRW(&m_cond, &state.m_mutex, boost::winapi::infinite, 0u); +} + +#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +typedef atomics::detail::core_operations< 4u, false, false > mutex_operations; +BOOST_STATIC_ASSERT_MSG(mutex_operations::is_always_lock_free, "Boost.Atomic unsupported target platform: native atomic operations not implemented for 32-bit integers"); + +namespace fallback_mutex_bits { + +//! The bit indicates a locked mutex +BOOST_CONSTEXPR_OR_CONST mutex_operations::storage_type locked = 1u; +//! The bit indicates that the critical section is initialized and should be used instead of the fallback mutex +BOOST_CONSTEXPR_OR_CONST mutex_operations::storage_type critical_section_initialized = 1u << 1; + +} // namespace mutex_bits + +//! State of a wait operation associated with an atomic object +struct wait_state : + public wait_state_base +{ + /*! + * \brief A semaphore used to block one or more threads + * + * A semaphore can be used to block a thread if it has no ongoing notifications (i.e. \c m_notify_count is 0). + * If there is no such semaphore, the thread has to allocate a new one to block on. This is to guarantee + * that a thread that is blocked after a notification is not immediately released by the semaphore while + * there are previously blocked threads. + * + * Semaphores are organized in a circular doubly linked list. A single semaphore object represents a list + * of one semaphore and is said to be "singular". + */ + struct semaphore + { + //! Pointer to the next semaphore in the list + semaphore* m_next; + //! Pointer to the previous semaphore in the list + semaphore* m_prev; + + //! Semaphore handle + boost::winapi::HANDLE_ m_semaphore; + //! Number of threads blocked on the semaphore + boost::winapi::ULONG_ m_waiter_count; + //! Number of threads released by notifications + boost::winapi::ULONG_ m_notify_count; + + semaphore() BOOST_NOEXCEPT : + m_semaphore(boost::winapi::create_anonymous_semaphore(NULL, 0, (std::numeric_limits< boost::winapi::LONG_ >::max)())), + m_waiter_count(0u), + m_notify_count(0u) + { + m_next = m_prev = this; + } + + ~semaphore() BOOST_NOEXCEPT + { + BOOST_ASSERT(is_singular()); + + if (BOOST_LIKELY(m_semaphore != boost::winapi::invalid_handle_value)) + boost::winapi::CloseHandle(m_semaphore); + } + + //! Creates a new semaphore or returns null in case of failure + static semaphore* create() BOOST_NOEXCEPT + { + semaphore* p = new (std::nothrow) semaphore(); + if (BOOST_UNLIKELY(p != NULL && p->m_semaphore == boost::winapi::invalid_handle_value)) + { + delete p; + p = NULL; + } + return p; + } + + //! Returns \c true if the semaphore is the single element of the list + bool is_singular() const BOOST_NOEXCEPT + { + return m_next == this /* && m_prev == this */; + } + + //! Inserts the semaphore list after the specified other semaphore + void link_after(semaphore* that) BOOST_NOEXCEPT + { + link_before(that->m_next); + } + + //! Inserts the semaphore list before the specified other semaphore + void link_before(semaphore* that) BOOST_NOEXCEPT + { + semaphore* prev = that->m_prev; + that->m_prev = m_prev; + m_prev->m_next = that; + m_prev = prev; + prev->m_next = this; + } + + //! Removes the semaphore from the list + void unlink() BOOST_NOEXCEPT + { + // Load pointers beforehand, in case we are the only element in the list + semaphore* next = m_next; + semaphore* prev = m_prev; + prev->m_next = next; + next->m_prev = prev; + m_next = m_prev = this; + } + + BOOST_DELETED_FUNCTION(semaphore(semaphore const&)) + BOOST_DELETED_FUNCTION(semaphore& operator= (semaphore const&)) + }; + + //! Doubly linked circular list of semaphores + class semaphore_list + { + private: + semaphore* m_head; + + public: + semaphore_list() BOOST_NOEXCEPT : + m_head(NULL) + { + } + + //! Returns \c true if the list is empty + bool empty() const BOOST_NOEXCEPT + { + return m_head == NULL; + } + + //! Returns the first semaphore in the list + semaphore* front() const BOOST_NOEXCEPT + { + return m_head; + } + + //! Returns the first semaphore in the list and leaves the list empty + semaphore* eject() BOOST_NOEXCEPT + { + semaphore* sem = m_head; + m_head = NULL; + return sem; + } + + //! Inserts the semaphore at the beginning of the list + void push_front(semaphore* sem) BOOST_NOEXCEPT + { + if (m_head) + sem->link_before(m_head); + + m_head = sem; + } + + //! Removes the first semaphore from the beginning of the list + semaphore* pop_front() BOOST_NOEXCEPT + { + BOOST_ASSERT(!empty()); + semaphore* sem = m_head; + erase(sem); + return sem; + } + + //! Removes the semaphore from the list + void erase(semaphore* sem) BOOST_NOEXCEPT + { + if (sem->is_singular()) + { + BOOST_ASSERT(m_head == sem); + m_head = NULL; + } + else + { + if (m_head == sem) + m_head = sem->m_next; + sem->unlink(); + } + } + + BOOST_DELETED_FUNCTION(semaphore_list(semaphore_list const&)) + BOOST_DELETED_FUNCTION(semaphore_list& operator= (semaphore_list const&)) + }; + + //! List of semaphores used for notifying. Here, every semaphore has m_notify_count > 0 && m_waiter_count > 0. + semaphore_list m_notify_semaphores; + //! List of semaphores used for waiting. Here, every semaphore has m_notify_count == 0 && m_waiter_count > 0. + semaphore_list m_wait_semaphores; + //! List of free semaphores. Here, every semaphore has m_notify_count == 0 && m_waiter_count == 0. + semaphore_list m_free_semaphores; + + explicit wait_state(std::size_t index) BOOST_NOEXCEPT : + wait_state_base(index) + { + } + + ~wait_state() BOOST_NOEXCEPT + { + // All wait and notification operations must have been completed + BOOST_ASSERT(m_notify_semaphores.empty()); + BOOST_ASSERT(m_wait_semaphores.empty()); + + semaphore* sem = m_free_semaphores.eject(); + if (sem) + { + while (true) + { + bool was_last = sem->is_singular(); + semaphore* next = sem->m_next; + sem->unlink(); + + delete sem; + + if (was_last) + break; + + sem = next; + } + } + } + + //! Blocks in the wait operation until notified + void wait(lock_state& state) BOOST_NOEXCEPT; + //! Fallback implementation of wait + void wait_fallback(lock_state& state) BOOST_NOEXCEPT; + + //! Wakes up one thread blocked in the wait operation + void notify_one(lock_state&) BOOST_NOEXCEPT + { + if (m_notify_semaphores.empty()) + { + if (m_wait_semaphores.empty()) + return; + + // Move the semaphore with waiters to the notify list + m_notify_semaphores.push_front(m_wait_semaphores.pop_front()); + } + + semaphore* sem = m_notify_semaphores.front(); + ++sem->m_notify_count; + + if (sem->m_notify_count == sem->m_waiter_count) + { + // Remove this semaphore from the list. The waiter will re-insert it into the waiter or free list once there are no more pending notifications in it. + m_notify_semaphores.erase(sem); + } + + boost::winapi::ReleaseSemaphore(sem->m_semaphore, 1, NULL); + } + + //! Wakes up all threads blocked in the wait operation + void notify_all(lock_state&) BOOST_NOEXCEPT + { + // Combine all notify and waiter semaphores in one list + semaphore* sem = m_notify_semaphores.eject(); + if (sem) + { + if (!m_wait_semaphores.empty()) + { + m_wait_semaphores.eject()->link_before(sem); + } + } + else + { + sem = m_wait_semaphores.eject(); + } + + if (sem) + { + while (true) + { + bool was_last = sem->is_singular(); + semaphore* next = sem->m_next; + sem->unlink(); + + boost::winapi::ULONG_ count = sem->m_waiter_count - sem->m_notify_count; + sem->m_notify_count += count; + + boost::winapi::ReleaseSemaphore(sem->m_semaphore, count, NULL); + + if (was_last) + break; + + sem = next; + } + } + } +}; + +//! Lock pool entry +struct lock_state +{ + //! Mutex + boost::winapi::CRITICAL_SECTION_ m_mutex; + //! Fallback mutex. Used as indicator of critical section initialization state and a fallback mutex, if critical section cannot be initialized. + BOOST_ATOMIC_DETAIL_ALIGNED_VAR(mutex_operations::storage_alignment, mutex_operations::storage_type, m_mutex_fallback); + //! Wait states + wait_state_list m_wait_states; + + //! Locks the mutex for a short duration + void short_lock() BOOST_NOEXCEPT + { + long_lock(); + } + + //! Locks the mutex for a long duration + void long_lock() BOOST_NOEXCEPT + { + mutex_operations::storage_type fallback_state = mutex_operations::load(m_mutex_fallback, boost::memory_order_relaxed); + while (true) + { + if (BOOST_LIKELY(fallback_state == fallback_mutex_bits::critical_section_initialized)) + { + lock_cs: + boost::winapi::EnterCriticalSection(&m_mutex); + return; + } + + while (fallback_state == 0u) + { + if (!mutex_operations::compare_exchange_weak(m_mutex_fallback, fallback_state, fallback_mutex_bits::locked, boost::memory_order_acquire, boost::memory_order_relaxed)) + continue; + + if (BOOST_LIKELY(!!boost::winapi::InitializeCriticalSectionAndSpinCount(&m_mutex, 100u))) + { + mutex_operations::store(m_mutex_fallback, fallback_mutex_bits::critical_section_initialized, boost::memory_order_release); + goto lock_cs; + } + + // We failed to init the critical section, leave the fallback mutex locked and return + return; + } + + if (fallback_state == fallback_mutex_bits::locked) + { + // Wait intil the fallback mutex is unlocked + boost::winapi::SwitchToThread(); + fallback_state = mutex_operations::load(m_mutex_fallback, boost::memory_order_relaxed); + } + } + } + + //! Unlocks the mutex + void unlock() BOOST_NOEXCEPT + { + mutex_operations::storage_type fallback_state = mutex_operations::load(m_mutex_fallback, boost::memory_order_relaxed); + if (BOOST_LIKELY(fallback_state == fallback_mutex_bits::critical_section_initialized)) + { + boost::winapi::LeaveCriticalSection(&m_mutex); + return; + } + + mutex_operations::store(m_mutex_fallback, 0u, boost::memory_order_release); + } +}; + +#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_ALIGNAS) +#define BOOST_ATOMIC_LOCK_STATE_INIT { {}, 0u, BOOST_ATOMIC_WAIT_STATE_LIST_INIT } +#else +#define BOOST_ATOMIC_LOCK_STATE_INIT { {}, { 0u }, BOOST_ATOMIC_WAIT_STATE_LIST_INIT } +#endif + +//! Blocks in the wait operation until notified +inline void wait_state::wait(lock_state& state) BOOST_NOEXCEPT +{ + // Find a semaphore to block on + semaphore* sem = m_wait_semaphores.front(); + if (sem) + { + while (sem->m_waiter_count >= static_cast< boost::winapi::ULONG_ >((std::numeric_limits< boost::winapi::LONG_ >::max)())) + { + if (sem->m_next == m_wait_semaphores.front()) + { + sem = NULL; + break; + } + + sem = sem->m_next; + } + } + + if (!sem) + { + if (BOOST_LIKELY(!m_free_semaphores.empty())) + { + sem = m_free_semaphores.pop_front(); + } + else + { + sem = semaphore::create(); + if (BOOST_UNLIKELY(!sem)) + { + wait_fallback(state); + return; + } + } + + m_wait_semaphores.push_front(sem); + } + + ++sem->m_waiter_count; + + state.unlock(); + + boost::winapi::WaitForSingleObject(sem->m_semaphore, boost::winapi::infinite); + + state.long_lock(); + + --sem->m_waiter_count; + + if (sem->m_notify_count > 0u) + { + // This semaphore is either in the notify list or not in a list at all + if (--sem->m_notify_count == 0u) + { + if (!sem->is_singular() || sem == m_notify_semaphores.front()) + m_notify_semaphores.erase(sem); + + semaphore_list* list = sem->m_waiter_count == 0u ? &m_free_semaphores : &m_wait_semaphores; + list->push_front(sem); + } + } + else if (sem->m_waiter_count == 0u) + { + // Move the semaphore to the free list + m_wait_semaphores.erase(sem); + m_free_semaphores.push_front(sem); + } +} + +//! Fallback implementation of wait +inline void wait_state::wait_fallback(lock_state& state) BOOST_NOEXCEPT +{ + state.unlock(); + + boost::winapi::Sleep(0); + + state.long_lock(); +} + +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +#endif + +enum +{ + tail_size = sizeof(lock_state) % BOOST_ATOMIC_CACHE_LINE_SIZE, + padding_size = tail_size > 0 ? BOOST_ATOMIC_CACHE_LINE_SIZE - tail_size : 0u +}; + +template< unsigned int PaddingSize > +struct BOOST_ALIGNMENT(BOOST_ATOMIC_CACHE_LINE_SIZE) padded_lock_state +{ + lock_state state; + // The additional padding is needed to avoid false sharing between locks + char padding[PaddingSize]; +}; + +template< > +struct BOOST_ALIGNMENT(BOOST_ATOMIC_CACHE_LINE_SIZE) padded_lock_state< 0u > +{ + lock_state state; +}; + +typedef padded_lock_state< padding_size > padded_lock_state_t; + +#if !defined(BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2) +#define BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2 8 +#endif +#if (BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2) < 0 +#error "Boost.Atomic: BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2 macro value is negative" +#endif +#define BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE (1ull << (BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2)) + +//! Lock pool size. Must be a power of two. +BOOST_CONSTEXPR_OR_CONST std::size_t lock_pool_size = static_cast< std::size_t >(1u) << (BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2); + +static padded_lock_state_t g_lock_pool[lock_pool_size] = +{ +#if BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE > 256u +#if (BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE / 256u) > BOOST_PP_LIMIT_ITERATION +#error "Boost.Atomic: BOOST_ATOMIC_LOCK_POOL_SIZE_LOG2 macro value is too large" +#endif +#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, (BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE / 256u), "lock_pool_init256.ipp")) +#else // BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE > 256u +#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE, "lock_pool_init1.ipp")) +#endif // BOOST_ATOMIC_DETAIL_LOCK_POOL_SIZE > 256u +#include BOOST_PP_ITERATE() +#undef BOOST_PP_ITERATION_PARAMS_1 +}; + +//! Pool cleanup function +void cleanup_lock_pool() +{ + for (std::size_t i = 0u; i < lock_pool_size; ++i) + { + lock_state& state = g_lock_pool[i].state; + state.long_lock(); + state.m_wait_states.m_free_memory = true; + state.m_wait_states.free_spare(); + state.unlock(); + } +} + +BOOST_STATIC_ASSERT_MSG(once_flag_operations::is_always_lock_free, "Boost.Atomic unsupported target platform: native atomic operations not implemented for bytes"); +static once_flag g_pool_cleanup_registered = {}; + +//! Returns index of the lock pool entry for the given pointer value +BOOST_FORCEINLINE std::size_t get_lock_index(atomics::detail::uintptr_t h) BOOST_NOEXCEPT +{ + return h & (lock_pool_size - 1u); +} + +//! Finds an existing element with the given pointer to the atomic object or allocates a new one +inline wait_state* wait_state_list::find_or_create(const volatile void* addr) BOOST_NOEXCEPT +{ + if (BOOST_UNLIKELY(m_header == NULL)) + { + m_header = allocate_buffer(initial_capacity); + if (BOOST_UNLIKELY(m_header == NULL)) + return NULL; + } + else + { + wait_state* ws = this->find(addr); + if (BOOST_LIKELY(ws != NULL)) + return ws; + + if (BOOST_UNLIKELY(m_header->size == m_header->capacity)) + { + header* new_header = allocate_buffer(m_header->capacity * 2u, m_header); + if (BOOST_UNLIKELY(new_header == NULL)) + return NULL; + boost::alignment::aligned_free(static_cast< void* >(m_header)); + m_header = new_header; + } + } + + const std::size_t index = m_header->size; + BOOST_ASSERT(index < m_header->capacity); + + wait_state** pw = get_wait_states() + index; + wait_state* w = *pw; + if (BOOST_UNLIKELY(w == NULL)) + { + w = new (std::nothrow) wait_state(index); + if (BOOST_UNLIKELY(w == NULL)) + return NULL; + *pw = w; + } + + get_atomic_pointers()[index] = addr; + + ++m_header->size; + + return w; +} + +//! Releases the previously created wait state +inline void wait_state_list::erase(wait_state* w) BOOST_NOEXCEPT +{ + BOOST_ASSERT(m_header != NULL); + + const volatile void** pa = get_atomic_pointers(); + wait_state** pw = get_wait_states(); + + std::size_t index = w->m_index; + + BOOST_ASSERT(index < m_header->size); + BOOST_ASSERT(pw[index] == w); + + std::size_t last_index = m_header->size - 1u; + + if (index != last_index) + { + pa[index] = pa[last_index]; + pa[last_index] = NULL; + + wait_state* last_w = pw[last_index]; + pw[index] = last_w; + pw[last_index] = w; + + last_w->m_index = index; + w->m_index = last_index; + } + else + { + pa[index] = NULL; + } + + --m_header->size; + + if (BOOST_UNLIKELY(m_free_memory)) + free_spare(); +} + +//! Allocates new buffer for the list entries +wait_state_list::header* wait_state_list::allocate_buffer(std::size_t new_capacity, header* old_header) BOOST_NOEXCEPT +{ + if (BOOST_UNLIKELY(once_flag_operations::load(g_pool_cleanup_registered.m_flag, boost::memory_order_relaxed) == 0u)) + { + if (once_flag_operations::exchange(g_pool_cleanup_registered.m_flag, 1u, boost::memory_order_relaxed) == 0u) + std::atexit(&cleanup_lock_pool); + } + + const std::size_t new_buffer_size = entries_offset + new_capacity * sizeof(void*) * 2u; + + void* p = boost::alignment::aligned_alloc(buffer_alignment, new_buffer_size); + if (BOOST_UNLIKELY(p == NULL)) + return NULL; + + header* h = new (p) header; + const volatile void** a = new (get_atomic_pointers(h)) const volatile void*[new_capacity]; + wait_state** w = new (get_wait_states(a, new_capacity)) wait_state*[new_capacity]; + + if (BOOST_LIKELY(old_header != NULL)) + { + BOOST_ASSERT(new_capacity >= old_header->capacity); + + h->size = old_header->size; + + const volatile void** old_a = get_atomic_pointers(old_header); + std::memcpy(a, old_a, old_header->size * sizeof(const volatile void*)); + std::memset(a + old_header->size * sizeof(const volatile void*), 0, (new_capacity - old_header->size) * sizeof(const volatile void*)); + + wait_state** old_w = get_wait_states(old_a, old_header->capacity); + std::memcpy(w, old_w, old_header->capacity * sizeof(wait_state*)); // copy spare wait state pointers + std::memset(w + old_header->capacity * sizeof(wait_state*), 0, (new_capacity - old_header->capacity) * sizeof(wait_state*)); + } + else + { + std::memset(p, 0, new_buffer_size); + } + + h->capacity = new_capacity; + + return h; +} + +//! Deallocates spare entries and the list buffer if no allocated entries are left +void wait_state_list::free_spare() BOOST_NOEXCEPT +{ + if (BOOST_LIKELY(m_header != NULL)) + { + wait_state** ws = get_wait_states(); + for (std::size_t i = m_header->size, n = m_header->capacity; i < n; ++i) + { + wait_state* w = ws[i]; + if (!w) + break; + + delete w; + ws[i] = NULL; + } + + if (m_header->size == 0u) + { + boost::alignment::aligned_free(static_cast< void* >(m_header)); + m_header = NULL; + } + } +} + +} // namespace + + +BOOST_ATOMIC_DECL void* short_lock(atomics::detail::uintptr_t h) BOOST_NOEXCEPT +{ + lock_state& ls = g_lock_pool[get_lock_index(h)].state; + ls.short_lock(); + return &ls; +} + +BOOST_ATOMIC_DECL void* long_lock(atomics::detail::uintptr_t h) BOOST_NOEXCEPT +{ + lock_state& ls = g_lock_pool[get_lock_index(h)].state; + ls.long_lock(); + return &ls; +} + +BOOST_ATOMIC_DECL void unlock(void* vls) BOOST_NOEXCEPT +{ + static_cast< lock_state* >(vls)->unlock(); +} + + +BOOST_ATOMIC_DECL void* allocate_wait_state(void* vls, const volatile void* addr) BOOST_NOEXCEPT +{ + BOOST_ASSERT(vls != NULL); + + lock_state* ls = static_cast< lock_state* >(vls); + + // Note: find_or_create may fail to allocate memory. However, C++20 specifies that wait/notify operations + // are noexcept, so allocate_wait_state must succeed. To implement this we return NULL in case of failure and test for NULL + // in other wait/notify functions so that all of them become nop (which is a conforming, though inefficient behavior). + wait_state* ws = ls->m_wait_states.find_or_create(addr); + + if (BOOST_LIKELY(ws != NULL)) + ++ws->m_ref_count; + + return ws; +} + +BOOST_ATOMIC_DECL void free_wait_state(void* vls, void* vws) BOOST_NOEXCEPT +{ + BOOST_ASSERT(vls != NULL); + + wait_state* ws = static_cast< wait_state* >(vws); + if (BOOST_LIKELY(ws != NULL)) + { + if (--ws->m_ref_count == 0u) + { + lock_state* ls = static_cast< lock_state* >(vls); + ls->m_wait_states.erase(ws); + } + } +} + +BOOST_ATOMIC_DECL void wait(void* vls, void* vws) BOOST_NOEXCEPT +{ + BOOST_ASSERT(vls != NULL); + + lock_state* ls = static_cast< lock_state* >(vls); + wait_state* ws = static_cast< wait_state* >(vws); + if (BOOST_LIKELY(ws != NULL)) + { + ws->wait(*ls); + } + else + { + // A conforming wait operation must unlock and lock the mutex to allow a notify to complete + ls->unlock(); + atomics::detail::wait_some(); + ls->long_lock(); + } +} + +BOOST_ATOMIC_DECL void notify_one(void* vls, const volatile void* addr) BOOST_NOEXCEPT +{ + BOOST_ASSERT(vls != NULL); + + lock_state* ls = static_cast< lock_state* >(vls); + wait_state* ws = ls->m_wait_states.find(addr); + if (BOOST_LIKELY(ws != NULL)) + ws->notify_one(*ls); +} + +BOOST_ATOMIC_DECL void notify_all(void* vls, const volatile void* addr) BOOST_NOEXCEPT +{ + BOOST_ASSERT(vls != NULL); + + lock_state* ls = static_cast< lock_state* >(vls); + wait_state* ws = ls->m_wait_states.find(addr); + if (BOOST_LIKELY(ws != NULL)) + ws->notify_all(*ls); +} + + +BOOST_ATOMIC_DECL void thread_fence() BOOST_NOEXCEPT +{ +#if BOOST_ATOMIC_THREAD_FENCE == 2 + atomics::detail::fence_operations::thread_fence(memory_order_seq_cst); +#else + // Emulate full fence by locking/unlocking a mutex + lock_pool::unlock(lock_pool::short_lock(0u)); +#endif +} + +BOOST_ATOMIC_DECL void signal_fence() BOOST_NOEXCEPT +{ + // This function is intentionally non-inline, even if empty. This forces the compiler to treat its call as a compiler barrier. +#if BOOST_ATOMIC_SIGNAL_FENCE == 2 + atomics::detail::fence_operations::signal_fence(memory_order_seq_cst); +#endif +} + +} // namespace lock_pool +} // namespace detail +} // namespace atomics +} // namespace boost + +#include diff --git a/libs/atomic/src/lock_pool_init1.ipp b/libs/atomic/src/lock_pool_init1.ipp new file mode 100644 index 0000000..531d120 --- /dev/null +++ b/libs/atomic/src/lock_pool_init1.ipp @@ -0,0 +1,13 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ + +#if BOOST_PP_ITERATION() > 1 +, +#endif + +{ BOOST_ATOMIC_LOCK_STATE_INIT } diff --git a/libs/atomic/src/lock_pool_init256.ipp b/libs/atomic/src/lock_pool_init256.ipp new file mode 100644 index 0000000..3458aee --- /dev/null +++ b/libs/atomic/src/lock_pool_init256.ipp @@ -0,0 +1,79 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ + +#if BOOST_PP_ITERATION() > 1 +, +#endif + +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, + +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, + +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, + +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, +{ BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT }, { BOOST_ATOMIC_LOCK_STATE_INIT } diff --git a/libs/atomic/src/wait_on_address.cpp b/libs/atomic/src/wait_on_address.cpp new file mode 100644 index 0000000..6f8a165 --- /dev/null +++ b/libs/atomic/src/wait_on_address.cpp @@ -0,0 +1,113 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file wait_on_address.cpp + * + * This file contains implementation of runtime detection of \c WaitOnAddress and related APIs on Windows. + * + * https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress + * https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-wakebyaddresssingle + * https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-wakebyaddressall + */ + +// Include boost/winapi/config.hpp first to make sure target Windows version is selected by Boost.WinAPI +#include + +#include + +#include +#include +#include +#include + +#if BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM + +#include +#include +#include +#include +#include + +#include + +#endif // BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM + +#include + +namespace boost { +namespace atomics { +namespace detail { + +BOOST_ATOMIC_DECL wait_on_address_t* wait_on_address = NULL; +BOOST_ATOMIC_DECL wake_by_address_t* wake_by_address_single = NULL; +BOOST_ATOMIC_DECL wake_by_address_t* wake_by_address_all = NULL; + +#if BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM + +BOOST_ATOMIC_DECL once_flag wait_functions_once_flag = { 2u }; + +BOOST_ATOMIC_DECL void initialize_wait_functions() BOOST_NOEXCEPT +{ + BOOST_STATIC_ASSERT_MSG(once_flag_operations::is_always_lock_free, "Boost.Atomic unsupported target platform: native atomic operations not implemented for bytes"); + + once_flag_operations::storage_type old_val = once_flag_operations::load(wait_functions_once_flag.m_flag, boost::memory_order_acquire); + while (true) + { + if (old_val == 2u) + { + if (BOOST_UNLIKELY(!once_flag_operations::compare_exchange_strong(wait_functions_once_flag.m_flag, old_val, 1u, boost::memory_order_relaxed, boost::memory_order_relaxed))) + continue; + + boost::winapi::HMODULE_ kernel_base = boost::winapi::get_module_handle(L"api-ms-win-core-synch-l1-2-0.dll"); + if (BOOST_LIKELY(kernel_base != NULL)) + { + wait_on_address_t* woa = (wait_on_address_t*)boost::winapi::get_proc_address(kernel_base, "WaitOnAddress"); + if (BOOST_LIKELY(woa != NULL)) + { + wake_by_address_t* wbas = (wake_by_address_t*)boost::winapi::get_proc_address(kernel_base, "WakeByAddressSingle"); + wake_by_address_t* wbaa = (wake_by_address_t*)boost::winapi::get_proc_address(kernel_base, "WakeByAddressAll"); + + if (BOOST_LIKELY(wbas != NULL && wbaa != NULL)) + { + wait_on_address = woa; + wake_by_address_single = wbas; + wake_by_address_all = wbaa; + } + } + } + + once_flag_operations::store(wait_functions_once_flag.m_flag, 0u, boost::memory_order_release); + break; + } + else if (old_val == 1u) + { + boost::winapi::SwitchToThread(); + old_val = once_flag_operations::load(wait_functions_once_flag.m_flag, boost::memory_order_acquire); + } + else + { + break; + } + } +} + +#else // BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM + +BOOST_ATOMIC_DECL once_flag wait_functions_once_flag = { 0u }; + +BOOST_ATOMIC_DECL void initialize_wait_functions() BOOST_NOEXCEPT +{ +} + +#endif // BOOST_WINAPI_PARTITION_DESKTOP || BOOST_WINAPI_PARTITION_SYSTEM + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include diff --git a/libs/atomic/src/x86_vector_tools.hpp b/libs/atomic/src/x86_vector_tools.hpp new file mode 100644 index 0000000..7d9e332 --- /dev/null +++ b/libs/atomic/src/x86_vector_tools.hpp @@ -0,0 +1,52 @@ +/* + * 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) + * + * Copyright (c) 2020 Andrey Semashev + */ +/*! + * \file x86_vector_tools.hpp + * + * This file contains common tools for x86 vectorization + */ + +#ifndef BOOST_ATOMIC_X86_VECTOR_TOOLS_HPP_INCLUDED_ +#define BOOST_ATOMIC_X86_VECTOR_TOOLS_HPP_INCLUDED_ + +#include +#include + +#if BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8) + +#include +#include +#include +#include + +#include + +namespace boost { +namespace atomics { +namespace detail { + +BOOST_FORCEINLINE __m128i mm_set1_epiptr(uintptr_t ptr) +{ +#if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210 + return _mm_set1_epi64x(ptr); +#else + // MSVC up until 14.0 update 3 doesn't provide _mm_set1_epi64x + uint32_t lo = static_cast< uint32_t >(ptr), hi = static_cast< uint32_t >(ptr >> 32); + return _mm_set_epi32(hi, lo, hi, lo); +#endif +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#include + +#endif // BOOST_ARCH_X86 && defined(BOOST_ATOMIC_DETAIL_SIZEOF_POINTER) && (BOOST_ATOMIC_DETAIL_SIZEOF_POINTER == 8) + +#endif // BOOST_ATOMIC_X86_VECTOR_TOOLS_HPP_INCLUDED_ diff --git a/libs/exception/build/Jamfile.v2 b/libs/exception/build/Jamfile.v2 new file mode 100644 index 0000000..fb47659 --- /dev/null +++ b/libs/exception/build/Jamfile.v2 @@ -0,0 +1,14 @@ +# Boost Exception Library build Jamfile +# +# Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. +# +# 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) + +project boost/exception + : source-location ../src + : requirements static + ; + +lib boost_exception : clone_current_exception_non_intrusive.cpp ; +boost-install boost_exception ; diff --git a/libs/exception/src/clone_current_exception_non_intrusive.cpp b/libs/exception/src/clone_current_exception_non_intrusive.cpp new file mode 100644 index 0000000..932bca8 --- /dev/null +++ b/libs/exception/src/clone_current_exception_non_intrusive.cpp @@ -0,0 +1,349 @@ +//Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. + +//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 MSVC-specific cpp file implements non-intrusive cloning of exception objects. +//Based on an exception_ptr implementation by Anthony Williams. + +#ifdef BOOST_NO_EXCEPTIONS +#error This file requires exception handling to be enabled. +#endif + +#include +#include + +#if defined(BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR) && defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + +//Non-intrusive cloning support implemented below, only for MSVC versions mentioned above. +//Thanks Anthony Williams! +//Thanks to Martin Weiss for implementing 64-bit support! + +#include +#include +#include +#include + +namespace + { + unsigned const exception_maximum_parameters=15; + unsigned const exception_noncontinuable=1; + +#if _MSC_VER==1310 + int const exception_info_offset=0x74; +#elif ((_MSC_VER==1400 || _MSC_VER==1500) && !defined _M_X64) + int const exception_info_offset=0x80; +#elif ((_MSC_VER==1400 || _MSC_VER==1500) && defined _M_X64) + int const exception_info_offset=0xE0; +#else + int const exception_info_offset=-1; +#endif + + struct + exception_record + { + unsigned long ExceptionCode; + unsigned long ExceptionFlags; + exception_record * ExceptionRecord; + void * ExceptionAddress; + unsigned long NumberParameters; + ULONG_PTR ExceptionInformation[exception_maximum_parameters]; + }; + + struct + exception_pointers + { + exception_record * ExceptionRecord; + void * ContextRecord; + }; + + unsigned const cpp_exception_code=0xE06D7363; + unsigned const cpp_exception_magic_flag=0x19930520; +#ifdef _M_X64 + unsigned const cpp_exception_parameter_count=4; +#else + unsigned const cpp_exception_parameter_count=3; +#endif + + struct + dummy_exception_type + { + }; + + typedef int(dummy_exception_type::*normal_copy_constructor_ptr)(void * src); + typedef int(dummy_exception_type::*copy_constructor_with_virtual_base_ptr)(void * src,void * dst); + typedef void (dummy_exception_type::*destructor_ptr)(); + + union + cpp_copy_constructor + { + void * address; + normal_copy_constructor_ptr normal_copy_constructor; + copy_constructor_with_virtual_base_ptr copy_constructor_with_virtual_base; + }; + + union + cpp_destructor + { + void * address; + destructor_ptr destructor; + }; + + enum + cpp_type_flags + { + class_is_simple_type=1, + class_has_virtual_base=4 + }; + + // ATTENTION: On x86 fields such as type_info and copy_constructor are really pointers + // but on 64bit these are 32bit offsets from HINSTANCE. Hints on the 64bit handling from + // http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx . + struct + cpp_type_info + { + unsigned flags; + int type_info; + int this_offset; + int vbase_descr; + int vbase_offset; + unsigned long size; + int copy_constructor; + }; + + struct + cpp_type_info_table + { + unsigned count; + int info; + }; + + struct + cpp_exception_type + { + unsigned flags; + int destructor; + int custom_handler; + int type_info_table; + }; + + struct + exception_object_deleter + { + cpp_exception_type const & et_; + size_t image_base_; + + exception_object_deleter( cpp_exception_type const & et, size_t image_base ): + et_(et), + image_base_(image_base) + { + } + + void + operator()( void * obj ) + { + BOOST_ASSERT(obj!=0); + dummy_exception_type* dummy_exception_ptr = static_cast(obj); + if( et_.destructor ) + { + cpp_destructor destructor; + destructor.address = reinterpret_cast(et_.destructor + image_base_); + (dummy_exception_ptr->*(destructor.destructor))(); + } + free(obj); + } + }; + + cpp_type_info const & + get_cpp_type_info( cpp_exception_type const & et, size_t image_base ) + { + cpp_type_info_table * const typearray = reinterpret_cast(et.type_info_table + image_base); + cpp_type_info * const ti = reinterpret_cast(typearray->info + image_base); + BOOST_ASSERT(ti!=0); + return *ti; + } + + void + copy_msvc_exception( void * dst, void * src, cpp_type_info const & ti, size_t image_base ) + { + cpp_copy_constructor copy_constructor; + copy_constructor.address = reinterpret_cast(ti.copy_constructor + image_base); + + if( !(ti.flags & class_is_simple_type) && copy_constructor.normal_copy_constructor ) + { + dummy_exception_type * dummy_exception_ptr = static_cast(dst); + if( ti.flags & class_has_virtual_base ) + (dummy_exception_ptr->*(copy_constructor.copy_constructor_with_virtual_base))(src,dst); + else + (dummy_exception_ptr->*(copy_constructor.normal_copy_constructor))(src); + } + else + memmove(dst,src,ti.size); + } + + boost::shared_ptr + clone_msvc_exception( void * src, cpp_exception_type const & et, size_t image_base ) + { + BOOST_ASSERT(src!=0); + cpp_type_info const & ti=get_cpp_type_info(et,image_base); + if( void * dst = malloc(ti.size) ) + { + try + { + copy_msvc_exception(dst,src,ti,image_base); + } + catch( + ... ) + { + free(dst); + throw; + } + return boost::shared_ptr(dst,exception_object_deleter(et,image_base)); + } + else + throw std::bad_alloc(); + } + + class + cloned_exception: + public boost::exception_detail::clone_base + { + cloned_exception( cloned_exception const & ); + cloned_exception & operator=( cloned_exception const & ); + + cpp_exception_type const & et_; + size_t image_base_; + boost::shared_ptr exc_; + + public: + cloned_exception( EXCEPTION_RECORD const * record ): + et_(*reinterpret_cast(record->ExceptionInformation[2])), + image_base_((cpp_exception_parameter_count==4) ? record->ExceptionInformation[3] : 0), + exc_(clone_msvc_exception(reinterpret_cast(record->ExceptionInformation[1]),et_,image_base_)) + { + } + + cloned_exception( void * exc, cpp_exception_type const & et, size_t image_base ): + et_(et), + image_base_(image_base), + exc_(clone_msvc_exception(exc,et_,image_base)) + { + } + + ~cloned_exception() BOOST_NOEXCEPT_OR_NOTHROW + { + } + + boost::exception_detail::clone_base const * + clone() const + { + return new cloned_exception(exc_.get(),et_,image_base_); + } + + void + rethrow() const + { + cpp_type_info const & ti=get_cpp_type_info(et_,image_base_); + void * dst = _alloca(ti.size); + copy_msvc_exception(dst,exc_.get(),ti,image_base_); + ULONG_PTR args[cpp_exception_parameter_count]; + args[0]=cpp_exception_magic_flag; + args[1]=reinterpret_cast(dst); + args[2]=reinterpret_cast(&et_); + if (cpp_exception_parameter_count==4) + args[3]=image_base_; + + RaiseException(cpp_exception_code,EXCEPTION_NONCONTINUABLE,cpp_exception_parameter_count,args); + } + }; + + bool + is_cpp_exception( EXCEPTION_RECORD const * record ) + { + return record && + (record->ExceptionCode==cpp_exception_code) && + (record->NumberParameters==cpp_exception_parameter_count) && + (record->ExceptionInformation[0]==cpp_exception_magic_flag); + } + + unsigned long + exception_cloning_filter( int & result, boost::exception_detail::clone_base const * & ptr, void * info_ ) + { + BOOST_ASSERT(exception_info_offset>=0); + BOOST_ASSERT(info_!=0); + EXCEPTION_RECORD* record = static_cast(info_)->ExceptionRecord; + if( is_cpp_exception(record) ) + { + if( !record->ExceptionInformation[2] ) + record = *reinterpret_cast(reinterpret_cast(_errno())+exception_info_offset); + if( is_cpp_exception(record) && record->ExceptionInformation[2] ) + try + { + ptr = new cloned_exception(record); + result = boost::exception_detail::clone_current_exception_result::success; + } + catch( + std::bad_alloc & ) + { + result = boost::exception_detail::clone_current_exception_result::bad_alloc; + } + catch( + ... ) + { + result = boost::exception_detail::clone_current_exception_result::bad_exception; + } + } + return EXCEPTION_EXECUTE_HANDLER; + } + } + +namespace +boost + { + namespace + exception_detail + { + int + clone_current_exception_non_intrusive( clone_base const * & cloned ) + { + BOOST_ASSERT(!cloned); + int result = clone_current_exception_result::not_supported; + if( exception_info_offset>=0 ) + { + clone_base const * ptr=0; + __try + { + throw; + } + __except(exception_cloning_filter(result,ptr,GetExceptionInformation())) + { + } + if( result==clone_current_exception_result::success ) + cloned=ptr; + } + BOOST_ASSERT(result!=clone_current_exception_result::success || cloned); + return result; + } + } + } + +#else + +//On all other compilers, return clone_current_exception_result::not_supported. +//On such platforms, only the intrusive enable_current_exception() cloning will work. + +namespace +boost + { + namespace + exception_detail + { + int + clone_current_exception_non_intrusive( clone_base const * & ) + { + return clone_current_exception_result::not_supported; + } + } + } + +#endif diff --git a/libs/locale/build/Jamfile.v2 b/libs/locale/build/Jamfile.v2 new file mode 100644 index 0000000..5bb135e --- /dev/null +++ b/libs/locale/build/Jamfile.v2 @@ -0,0 +1,433 @@ +# Copyright 2003 John Maddock +# Copyright 2010 Artyom Beilis +# Copyright 2021 - 2022 Alexander Grund +# +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt. + +import config : requires ; +import configure ; +import errors ; +import feature ; +import os ; +import path ; +import toolset ; + +path-constant TOP : .. ; + +project /boost/locale + : source-location $(TOP)/src/boost/locale + ; + +# Features + +feature.feature boost.locale.iconv : on off : optional propagated ; +feature.feature boost.locale.icu : on off : optional propagated ; +feature.feature boost.locale.posix : on off : optional propagated ; +feature.feature boost.locale.std : on off : optional propagated ; +feature.feature boost.locale.winapi : on off : optional propagated ; + +local rule debug-message ( message * ) +{ + if --debug-configuration in [ modules.peek : ARGV ] + { + ECHO "notice:" "[locale]" $(message) ; + } +} + +# Configuration of libraries + +## iconv +# Iconv support may be builtin (i.e. in libc) + +exe has_iconv : $(TOP)/build/has_iconv.cpp ; +explicit has_iconv ; + +ICONV_PATH = [ modules.peek : ICONV_PATH ] ; + +# There may also be an external iconv library +lib iconv : + : $(ICONV_PATH)/lib shared shared + : : $(ICONV_PATH)/include + ; + +explicit iconv ; + +# Separate pair of obj & exe rules so the CPP file is built with the iconv include path +obj has_external_iconv_obj : $(TOP)/build/has_iconv.cpp iconv ; +exe has_external_iconv : has_external_iconv_obj iconv ; +explicit has_external_iconv ; + +exe accepts_shared_option : $(TOP)/build/option.cpp + : -shared-libstdc++ + -shared-libgcc + -shared-libstdc++ + -shared-libgcc + ; + +# Xlocale + +exe has_xlocale : $(TOP)/build/has_xlocale.cpp ; +explicit has_xlocale ; + +#end xlocale + +if [ modules.peek : ICU_LINK ] +{ + errors.user-error : "The ICU_LINK option is no longer supported by the Boost.Locale build - please refer to the documentation for equivalent options" ; +} + +if [ modules.peek : ICU_LINK_LOCALE ] +{ + errors.user-error : "The ICU_LINK_LOCALE option is no longer supported by the Boost.Locale build - please refer to the documentation for equivalent options" ; +} + +# Specify the root path to the installed ICU libraries via +# - Environment variable: ICU_PATH +# - `b2 .. -s ICU_PATH=x` +# - In project-config.jam as `path-constant ICU_PATH : x ; + +.icu-path = [ modules.peek : ICU_PATH ] ; # -sICU_PATH=x or env variable +.icu-path ?= $(ICU_PATH) ; # path-constant + +if $(.icu-path) +{ + .icu-path = [ path.make $(.icu-path) ] ; # Normalize + debug-message ICU path set to "$(.icu-path)" ; +} + +rule path_options ( properties * ) +{ + local result ; + if 64 in $(properties) && msvc in $(properties) + { + debug-message Search 64 bit ICU in "$(.icu-path)/lib64" ; + result = $(.icu-path)/bin64 $(.icu-path)/lib64 ; + } + else + { + debug-message Search ICU in "$(.icu-path)/lib" ; + result = $(.icu-path)/bin $(.icu-path)/lib ; + } + return $(result) ; +} + +if [ modules.peek : ICU_ICUUC_NAME ] +{ + ICU_ICUUC_NAME = [ modules.peek : ICU_ICUUC_NAME ] ; +} +if [ modules.peek : ICU_ICUDT_NAME ] +{ + ICU_ICUDT_NAME = [ modules.peek : ICU_ICUDT_NAME ] ; +} +if [ modules.peek : ICU_ICUIN_NAME ] +{ + ICU_ICUIN_NAME = [ modules.peek : ICU_ICUIN_NAME ] ; +} + +if $(ICU_ICUUC_NAME) +{ + lib icuuc : : $(ICU_ICUUC_NAME) @path_options ; + debug-message Using "$(ICU_ICUUC_NAME)" for library "icuuc" ; +} +else +{ + lib icuuc : : shared @path_options ; + lib icuuc : : msvc debug icuucd shared @path_options ; + lib icuuc : : intel windows debug icuucd shared @path_options ; + lib icuuc : : sicuuc static @path_options ; + lib icuuc : : msvc debug sicuucd static @path_options ; + lib icuuc : : intel windows debug sicuucd static @path_options ; + lib icuuc : : this_is_an_invalid_library_name ; +} + +if $(ICU_ICUDT_NAME) +{ + lib icudt : : $(ICU_ICUDT_NAME) @path_options ; + debug-message Using "$(ICU_ICUDT_NAME)" for library "icudt" ; +} +else +{ + lib icudt : : icudata shared @path_options ; + lib icudt : : icudt msvc shared @path_options ; + lib icudt : : icudt intel windows shared @path_options ; + lib icudt : : sicudata static @path_options ; + lib icudt : : sicudt msvc static @path_options ; + lib icudt : : sicudt intel windows static @path_options ; + lib icudt : : this_is_an_invalid_library_name ; +} + +if $(ICU_ICUIN_NAME) +{ + lib icuin : : $(ICU_ICUIN_NAME) @path_options ; + debug-message Using "$(ICU_ICUIN_NAME)" for library "icuin" ; +} +else +{ + lib icuin : : icui18n shared @path_options ; + lib icuin : : msvc debug icuind shared @path_options ; + lib icuin : : msvc icuin shared @path_options ; + lib icuin : : intel windows debug icuind shared @path_options ; + lib icuin : : intel windows icuin shared @path_options ; + lib icuin : : sicui18n static @path_options ; + lib icuin : : msvc debug sicuind static @path_options ; + lib icuin : : msvc sicuin static @path_options ; + lib icuin : : intel windows debug sicuind static @path_options ; + lib icuin : : intel windows sicuin static @path_options ; + lib icuin : : this_is_an_invalid_library_name ; +} + +ICU_OPTS = + $(.icu-path)/include + shared:icuuc/shared + shared:icudt/shared + shared:icuin/shared + static:icuuc + static:icudt + static:icuin + windows,clang:"advapi32.lib" + static:U_STATIC_IMPLEMENTATION=1 + ; + +exe has_icu : $(TOP)/build/has_icu_test.cpp : $(ICU_OPTS) ; +explicit has_icu ; + + +# This function is called whenever the 'boost_locale' metatarget +# below is generated and figures out what external components we have, +# what the user wants, and what sources have to be compiled in the end. +rule configure-full ( properties * : flags-only ) +{ + + local result ; + local flags-result ; + + local found-iconv ; + + # The system Iconv on Solaris may have bugs, while the GNU Iconv is fine. + # So enable by default only if not on Solaris. + if on in $(properties) + || ( ! in $(properties:G) && ! solaris in $(properties) ) + { + # See if iconv is bundled with standard library. + if [ configure.builds has_iconv : $(properties) : "iconv (libc)" ] + { + found-iconv = true ; + } else if [ configure.builds has_external_iconv : $(properties) : "iconv (separate)" ] + { + found-iconv = true ; + result += iconv ; + } + } + if $(found-iconv) + { + flags-result += BOOST_LOCALE_WITH_ICONV=1 ; + } + + local found-icu ; + + if ! off in $(properties) + { + if [ configure.builds has_icu : $(properties) : "icu" ] + { + found-icu = true ; + flags-result += BOOST_LOCALE_WITH_ICU=1 $(ICU_OPTS) ; + + ICU_SOURCES = + boundary + codecvt + collator + conversion + date_time + formatter + formatters_cache + icu_backend + numeric + time_zone + ; + + result += icu/$(ICU_SOURCES).cpp + /boost/thread//boost_thread + ; + } + } + + if ! $(found-iconv) && ! $(found-icu) && ! windows in $(properties) && ! cygwin in $(properties) + { + ECHO "- Boost.Locale needs either iconv or ICU library to be built." ; + result += no ; + } + + if ! in $(properties:G) + { + if sun in $(properties) + { + properties += off ; + } else + { + properties += on ; + } + } + + if off in $(properties) + { + flags-result += BOOST_LOCALE_NO_STD_BACKEND=1 ; + } else + { + STD_SOURCES = + codecvt + collate + converter + numeric + std_backend + ; + result += std/$(STD_SOURCES).cpp ; + } + + if ! in $(properties:G) + { + if windows in $(properties) || cygwin in $(properties) + { + properties += on ; + } else + { + properties += off ; + } + } + if windows in $(properties) + && gcc in $(properties) + && shared in $(properties) + && [ configure.builds accepts_shared_option : $(properties) : "g++ -shared-* supported" ] + { + flags-result += -shared-libstdc++ + -shared-libgcc + -shared-libstdc++ + -shared-libgcc + ; + } + + if off in $(properties) + { + flags-result += BOOST_LOCALE_NO_WINAPI_BACKEND=1 ; + } else + { + WINAPI_SOURCES = + collate + converter + numeric + win_backend + ; + result += win32/$(WINAPI_SOURCES).cpp ; + } + + if ( ! off in $(properties) || ! off in $(properties) ) + && ( windows in $(properties) || cygwin in $(properties) ) + { + result += win32/lcid.cpp ; + } + + if ! in $(properties:G) + { + if linux in $(properties) + || darwin in $(properties) + || ( freebsd in $(properties) && [ configure.builds has_xlocale : $(properties) : "xlocale supported" ] ) + { + properties += on ; + } else + { + properties += off ; + } + } + + if off in $(properties) + { + flags-result += BOOST_LOCALE_NO_POSIX_BACKEND=1 ; + } else + { + POSIX_SOURCES = + collate + converter + numeric + codecvt + posix_backend + ; + result += posix/$(POSIX_SOURCES).cpp ; + } + + if on in $(properties) || on in $(properties) || on in $(properties) + { + result += util/gregorian.cpp ; + } + + if "$(flags-only)" = "flags" + { + return $(flags-result) ; + } else + { + result += $(flags-result) ; + return $(result) ; + } +} + +rule configure ( properties * ) +{ + local result = [ configure-full $(properties) : "all" ] ; + return $(result) ; +} + +rule configure-flags ( properties * ) +{ + local result ; + result = [ configure-full $(properties) : "flags" ] ; + return $(result) ; +} + +alias build_flags : : : : @configure-flags ; + +local cxx_requirements = [ requires + cxx11_auto_declarations + cxx11_decltype + cxx11_defaulted_functions + cxx11_defaulted_moves + cxx11_hdr_functional + cxx11_hdr_type_traits + cxx11_noexcept + cxx11_nullptr + cxx11_override + cxx11_range_based_for + cxx11_rvalue_references + cxx11_scoped_enums + cxx11_smart_ptr + cxx11_static_assert + ] ; + +boost-lib locale + : + encoding/codepage.cpp + shared/date_time.cpp + shared/format.cpp + shared/formatting.cpp + shared/generator.cpp + shared/iconv_codecvt.cpp + shared/ids.cpp + shared/localization_backend.cpp + shared/message.cpp + shared/mo_lambda.cpp + util/codecvt_converter.cpp + util/default_locale.cpp + util/encoding.cpp + util/info.cpp + util/locale_data.cpp + : + $(cxx_requirements) + BOOST_LOCALE_SOURCE + # Don't link explicitly, not required + BOOST_THREAD_NO_LIB=1 + $(TOP)/src + multi + windows:_CRT_SECURE_NO_WARNINGS + windows:_SCL_SECURE_NO_WARNINGS + # Meanwhile remove this + @configure + : : $(cxx_requirements) + ; diff --git a/libs/locale/build/has_iconv.cpp b/libs/locale/build/has_iconv.cpp new file mode 100644 index 0000000..4a1fc68 --- /dev/null +++ b/libs/locale/build/has_iconv.cpp @@ -0,0 +1,13 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +int main() +{ + iconv_t d = iconv_open(0, 0); + (void)(d); +} diff --git a/libs/locale/build/has_icu_test.cpp b/libs/locale/build/has_icu_test.cpp new file mode 100644 index 0000000..3f43f88 --- /dev/null +++ b/libs/locale/build/has_icu_test.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2010 John Maddock +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +int main() +{ + icu::Locale loc; + UErrorCode err = U_ZERO_ERROR; + UChar32 c = ::u_charFromName(U_UNICODE_CHAR_NAME, "GREEK SMALL LETTER ALPHA", &err); + return err; +} diff --git a/libs/locale/build/has_xlocale.cpp b/libs/locale/build/has_xlocale.cpp new file mode 100644 index 0000000..795a510 --- /dev/null +++ b/libs/locale/build/has_xlocale.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +int main() +{ + newlocale(LC_ALL_MASK, "", 0); +} diff --git a/libs/locale/build/option.cpp b/libs/locale/build/option.cpp new file mode 100644 index 0000000..a548feb --- /dev/null +++ b/libs/locale/build/option.cpp @@ -0,0 +1,7 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +int main() {} diff --git a/libs/locale/src/boost/locale/encoding/codepage.cpp b/libs/locale/src/boost/locale/encoding/codepage.cpp new file mode 100644 index 0000000..631b526 --- /dev/null +++ b/libs/locale/src/boost/locale/encoding/codepage.cpp @@ -0,0 +1,163 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#include "boost/locale/encoding/conv.hpp" +#if BOOST_LOCALE_USE_WIN32_API +# define BOOST_LOCALE_WITH_WCONV +#endif +#ifdef BOOST_LOCALE_WITH_ICONV +# include "boost/locale/encoding/iconv_codepage.ipp" +#endif +#ifdef BOOST_LOCALE_WITH_ICU +# include "boost/locale/encoding/uconv_codepage.ipp" +#endif +#ifdef BOOST_LOCALE_WITH_WCONV +# include "boost/locale/encoding/wconv_codepage.ipp" +#endif + +namespace boost { namespace locale { namespace conv { + namespace impl { + + std::string convert_between(const char* begin, + const char* end, + const char* to_charset, + const char* from_charset, + method_type how) + { + hold_ptr cvt; +#ifdef BOOST_LOCALE_WITH_ICONV + cvt.reset(new iconv_between()); + if(cvt->open(to_charset, from_charset, how)) + return cvt->convert(begin, end); +#endif +#ifdef BOOST_LOCALE_WITH_ICU + cvt.reset(new uconv_between()); + if(cvt->open(to_charset, from_charset, how)) + return cvt->convert(begin, end); +#endif +#ifdef BOOST_LOCALE_WITH_WCONV + cvt.reset(new wconv_between()); + if(cvt->open(to_charset, from_charset, how)) + return cvt->convert(begin, end); +#endif + throw invalid_charset_error(std::string(to_charset) + " or " + from_charset); + } + + template + std::basic_string convert_to(const char* begin, const char* end, const char* charset, method_type how) + { + hold_ptr> cvt; +#ifdef BOOST_LOCALE_WITH_ICONV + cvt.reset(new iconv_to_utf()); + if(cvt->open(charset, how)) + return cvt->convert(begin, end); +#endif +#ifdef BOOST_LOCALE_WITH_ICU + cvt.reset(new uconv_to_utf()); + if(cvt->open(charset, how)) + return cvt->convert(begin, end); +#endif +#ifdef BOOST_LOCALE_WITH_WCONV + cvt.reset(new wconv_to_utf()); + if(cvt->open(charset, how)) + return cvt->convert(begin, end); +#endif + throw invalid_charset_error(charset); + } + + template + std::string convert_from(const CharType* begin, const CharType* end, const char* charset, method_type how) + { + hold_ptr> cvt; +#ifdef BOOST_LOCALE_WITH_ICONV + cvt.reset(new iconv_from_utf()); + if(cvt->open(charset, how)) + return cvt->convert(begin, end); +#endif +#ifdef BOOST_LOCALE_WITH_ICU + cvt.reset(new uconv_from_utf()); + if(cvt->open(charset, how)) + return cvt->convert(begin, end); +#endif +#ifdef BOOST_LOCALE_WITH_WCONV + cvt.reset(new wconv_from_utf()); + if(cvt->open(charset, how)) + return cvt->convert(begin, end); +#endif + throw invalid_charset_error(charset); + } + + } // namespace impl + + using namespace impl; + + std::string between(const char* begin, + const char* end, + const std::string& to_charset, + const std::string& from_charset, + method_type how) + { + return convert_between(begin, end, to_charset.c_str(), from_charset.c_str(), how); + } + + template<> + std::basic_string to_utf(const char* begin, const char* end, const std::string& charset, method_type how) + { + return convert_to(begin, end, charset.c_str(), how); + } + + template<> + std::string from_utf(const char* begin, const char* end, const std::string& charset, method_type how) + { + return convert_from(begin, end, charset.c_str(), how); + } + + template<> + std::basic_string to_utf(const char* begin, const char* end, const std::string& charset, method_type how) + { + return convert_to(begin, end, charset.c_str(), how); + } + + template<> + std::string from_utf(const wchar_t* begin, const wchar_t* end, const std::string& charset, method_type how) + { + return convert_from(begin, end, charset.c_str(), how); + } + +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + template<> + std::basic_string to_utf(const char* begin, const char* end, const std::string& charset, method_type how) + { + return convert_to(begin, end, charset.c_str(), how); + } + + template<> + std::string from_utf(const char16_t* begin, const char16_t* end, const std::string& charset, method_type how) + { + return convert_from(begin, end, charset.c_str(), how); + } +#endif + +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + template<> + std::basic_string to_utf(const char* begin, const char* end, const std::string& charset, method_type how) + { + return convert_to(begin, end, charset.c_str(), how); + } + + template<> + std::string from_utf(const char32_t* begin, const char32_t* end, const std::string& charset, method_type how) + { + return convert_from(begin, end, charset.c_str(), how); + } +#endif + +}}} // namespace boost::locale::conv diff --git a/libs/locale/src/boost/locale/encoding/conv.hpp b/libs/locale/src/boost/locale/encoding/conv.hpp new file mode 100644 index 0000000..6d2ab68 --- /dev/null +++ b/libs/locale/src/boost/locale/encoding/conv.hpp @@ -0,0 +1,73 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_CONV_IMPL_HPP +#define BOOST_LOCALE_CONV_IMPL_HPP + +#include +#include + +namespace boost { namespace locale { namespace conv { namespace impl { + + template + const char* utf_name() + { + switch(sizeof(CharType)) { + case 1: return "UTF-8"; + case 2: { + const int16_t endianMark = 1; + const bool isLittleEndian = reinterpret_cast(&endianMark)[0] == 1; + return isLittleEndian ? "UTF-16LE" : "UTF-16BE"; + } + case 4: { + const int32_t endianMark = 1; + const bool isLittleEndian = reinterpret_cast(&endianMark)[0] == 1; + return isLittleEndian ? "UTF-32LE" : "UTF-32BE"; + } + } + return "Unknown Character Encoding"; + } + + class converter_between { + public: + typedef char char_type; + typedef std::string string_type; + + virtual bool open(const char* to_charset, const char* from_charset, method_type how) = 0; + + virtual std::string convert(const char* begin, const char* end) = 0; + + virtual ~converter_between() = default; + }; + + template + class converter_from_utf { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + virtual bool open(const char* charset, method_type how) = 0; + + virtual std::string convert(const CharType* begin, const CharType* end) = 0; + + virtual ~converter_from_utf() = default; + }; + + template + class converter_to_utf { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + virtual bool open(const char* charset, method_type how) = 0; + + virtual string_type convert(const char* begin, const char* end) = 0; + + virtual ~converter_to_utf() = default; + }; +}}}} // namespace boost::locale::conv::impl + +#endif diff --git a/libs/locale/src/boost/locale/encoding/iconv_codepage.ipp b/libs/locale/src/boost/locale/encoding/iconv_codepage.ipp new file mode 100644 index 0000000..a5f7ab3 --- /dev/null +++ b/libs/locale/src/boost/locale/encoding/iconv_codepage.ipp @@ -0,0 +1,175 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_ICONV_CODEPAGE_HPP +#define BOOST_LOCALE_IMPL_ICONV_CODEPAGE_HPP + +#include +#include "boost/locale/encoding/conv.hpp" +#include "boost/locale/util/iconv.hpp" +#include + +namespace boost { namespace locale { namespace conv { namespace impl { + + class iconverter_base { + public: + iconverter_base() : cvt_((iconv_t)(-1)) {} + ~iconverter_base() { close(); } + + bool do_open(const char* to, const char* from, method_type how) + { + close(); + cvt_ = iconv_open(to, from); + how_ = how; + return cvt_ != (iconv_t)(-1); + } + + template + std::basic_string real_convert(const InChar* ubegin, const InChar* uend) + { + std::basic_string sresult; + + sresult.reserve(uend - ubegin); + + OutChar result[64]; + + char* out_start = reinterpret_cast(&result[0]); + const char* begin = reinterpret_cast(ubegin); + const char* end = reinterpret_cast(uend); + + enum { normal, unshifting, done } state = normal; + + while(state != done) { + size_t in_left = end - begin; + size_t out_left = sizeof(result); + + char* out_ptr = out_start; + size_t res = 0; + if(in_left == 0) + state = unshifting; + + if(state == normal) + res = conv(&begin, &in_left, &out_ptr, &out_left); + else + res = conv(0, 0, &out_ptr, &out_left); + + int err = errno; + + size_t output_count = (out_ptr - out_start) / sizeof(OutChar); + + if(res != 0 && res != (size_t)(-1)) { + if(how_ == stop) { + throw conversion_error(); + } + } + + sresult.append(&result[0], output_count); + + if(res == (size_t)(-1)) { + if(err == EILSEQ || err == EINVAL) { + if(how_ == stop) { + throw conversion_error(); + } + + if(begin != end) { + begin += sizeof(InChar); + if(begin >= end) + break; + } else { + break; + } + } else if(err == E2BIG) { + continue; + } else { + // We should never get there + // but if we do + if(how_ == stop) + throw conversion_error(); + else + break; + } + } + if(state == unshifting) + state = done; + } + return sresult; + } + + private: + void close() + { + if(cvt_ != (iconv_t)(-1)) { + iconv_close(cvt_); + cvt_ = (iconv_t)(-1); + } + } + + size_t conv(const char** inbuf, size_t* inchar_left, char** outbuf, size_t* outchar_left) + { + return call_iconv(cvt_, inbuf, inchar_left, outbuf, outchar_left); + } + + iconv_t cvt_; + method_type how_; + }; + + template + class iconv_from_utf : public converter_from_utf { + public: + typedef CharType char_type; + + bool open(const char* charset, method_type how) override + { + return self_.do_open(charset, utf_name(), how); + } + + std::string convert(const char_type* ubegin, const char_type* uend) override + { + return self_.template real_convert(ubegin, uend); + } + + private: + iconverter_base self_; + }; + + class iconv_between : public converter_between { + public: + bool open(const char* to_charset, const char* from_charset, method_type how) override + { + return self_.do_open(to_charset, from_charset, how); + } + std::string convert(const char* begin, const char* end) override + { + return self_.real_convert(begin, end); + } + + private: + iconverter_base self_; + }; + + template + class iconv_to_utf : public converter_to_utf { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + bool open(const char* charset, method_type how) override + { + return self_.do_open(utf_name(), charset, how); + } + + string_type convert(const char* begin, const char* end) override + { + return self_.template real_convert(begin, end); + } + + private: + iconverter_base self_; + }; + +}}}} // namespace boost::locale::conv::impl + +#endif diff --git a/libs/locale/src/boost/locale/encoding/uconv_codepage.ipp b/libs/locale/src/boost/locale/encoding/uconv_codepage.ipp new file mode 100644 index 0000000..1c2b7ed --- /dev/null +++ b/libs/locale/src/boost/locale/encoding/uconv_codepage.ipp @@ -0,0 +1,141 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_UCONV_CODEPAGE_HPP +#define BOOST_LOCALE_IMPL_UCONV_CODEPAGE_HPP +#include +#include +#include "boost/locale/encoding/conv.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/uconv.hpp" +#include +#include + +namespace boost { namespace locale { namespace conv { namespace impl { + template + class uconv_to_utf : public converter_to_utf { + public: + typedef CharType char_type; + + typedef std::basic_string string_type; + + bool open(const char* charset, method_type how) override + { + close(); + try { + using impl_icu::cpcvt_type; + cvt_from_.reset(new from_type(charset, how == skip ? cpcvt_type::skip : cpcvt_type::stop)); + cvt_to_.reset(new to_type("UTF-8", how == skip ? cpcvt_type::skip : cpcvt_type::stop)); + } catch(const std::exception& /*e*/) { + close(); + return false; + } + return true; + } + void close() + { + cvt_from_.reset(); + cvt_to_.reset(); + } + + string_type convert(const char* begin, const char* end) override + { + try { + return cvt_to_->std(cvt_from_->icu_checked(begin, end)); + } catch(const std::exception& /*e*/) { + throw conversion_error(); + } + } + + private: + typedef impl_icu::icu_std_converter from_type; + typedef impl_icu::icu_std_converter to_type; + + hold_ptr cvt_from_; + hold_ptr cvt_to_; + }; + + template + class uconv_from_utf : public converter_from_utf { + public: + typedef CharType char_type; + bool open(const char* charset, method_type how) override + { + close(); + try { + using impl_icu::cpcvt_type; + cvt_from_.reset(new from_type("UTF-8", how == skip ? cpcvt_type::skip : cpcvt_type::stop)); + cvt_to_.reset(new to_type(charset, how == skip ? cpcvt_type::skip : cpcvt_type::stop)); + } catch(const std::exception& /*e*/) { + close(); + return false; + } + return true; + } + void close() + { + cvt_from_.reset(); + cvt_to_.reset(); + } + + std::string convert(const CharType* begin, const CharType* end) override + { + try { + return cvt_to_->std(cvt_from_->icu_checked(begin, end)); + } catch(const std::exception& /*e*/) { + throw conversion_error(); + } + } + + private: + typedef impl_icu::icu_std_converter from_type; + typedef impl_icu::icu_std_converter to_type; + + hold_ptr cvt_from_; + hold_ptr cvt_to_; + }; + + class uconv_between : public converter_between { + public: + bool open(const char* to_charset, const char* from_charset, method_type how) override + { + close(); + try { + using impl_icu::cpcvt_type; + cvt_from_.reset(new from_type(from_charset, how == skip ? cpcvt_type::skip : cpcvt_type::stop)); + cvt_to_.reset(new to_type(to_charset, how == skip ? cpcvt_type::skip : cpcvt_type::stop)); + } catch(const std::exception& /*e*/) { + close(); + return false; + } + return true; + } + void close() + { + cvt_from_.reset(); + cvt_to_.reset(); + } + + std::string convert(const char* begin, const char* end) override + { + try { + return cvt_to_->std(cvt_from_->icu(begin, end)); + } catch(const std::exception& /*e*/) { + throw conversion_error(); + } + } + + private: + typedef impl_icu::icu_std_converter from_type; + typedef impl_icu::icu_std_converter to_type; + + hold_ptr cvt_from_; + hold_ptr cvt_to_; + }; + +}}}} // namespace boost::locale::conv::impl + +#endif diff --git a/libs/locale/src/boost/locale/encoding/wconv_codepage.ipp b/libs/locale/src/boost/locale/encoding/wconv_codepage.ipp new file mode 100644 index 0000000..bf97fd5 --- /dev/null +++ b/libs/locale/src/boost/locale/encoding/wconv_codepage.ipp @@ -0,0 +1,432 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_WCONV_CODEPAGE_HPP +#define BOOST_LOCALE_IMPL_WCONV_CODEPAGE_HPP + +#ifndef NOMINMAX +# define NOMINMAX +#endif +#include +#include "boost/locale/encoding/conv.hpp" +#include "boost/locale/util/encoding.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace conv { namespace impl { + + size_t remove_substitutions(std::vector& v) + { + if(std::find(v.begin(), v.end(), 0) == v.end()) { + return v.size(); + } + std::vector v2; + v2.reserve(v.size()); + for(unsigned i = 0; i < v.size(); i++) { + if(v[i] != 0) + v2.push_back(v[i]); + } + v.swap(v2); + return v.size(); + } + + void multibyte_to_wide_one_by_one(int codepage, const char* begin, const char* end, std::vector& buf) + { + buf.reserve(end - begin); + DWORD flags = MB_ERR_INVALID_CHARS; + while(begin != end) { + wchar_t wide_buf[4]; + int n = 0; + int len = IsDBCSLeadByteEx(codepage, *begin) ? 2 : 1; + if(len == 2 && begin + 1 == end) + return; + n = MultiByteToWideChar(codepage, flags, begin, len, wide_buf, 4); + if(n == 0 && flags != 0 && GetLastError() == ERROR_INVALID_FLAGS) { + flags = 0; + n = MultiByteToWideChar(codepage, flags, begin, len, wide_buf, 4); + } + for(int i = 0; i < n; i++) + buf.push_back(wide_buf[i]); + begin += len; + } + } + + void multibyte_to_wide(int codepage, const char* begin, const char* end, bool do_skip, std::vector& buf) + { + if(begin == end) + return; + const std::ptrdiff_t num_chars = end - begin; + if(num_chars > std::numeric_limits::max()) + throw conversion_error(); + DWORD flags = MB_ERR_INVALID_CHARS; + int n = MultiByteToWideChar(codepage, flags, begin, static_cast(num_chars), 0, 0); + if(n == 0 && GetLastError() == ERROR_INVALID_FLAGS) { + flags = 0; + n = MultiByteToWideChar(codepage, flags, begin, static_cast(num_chars), 0, 0); + } + + if(n == 0) { + if(do_skip) { + multibyte_to_wide_one_by_one(codepage, begin, end, buf); + return; + } + throw conversion_error(); + } + + buf.resize(n); + if(MultiByteToWideChar(codepage, flags, begin, static_cast(num_chars), &buf.front(), n) == 0) + throw conversion_error(); + } + + void wide_to_multibyte_non_zero(int codepage, + const wchar_t* begin, + const wchar_t* end, + bool do_skip, + std::vector& buf) + { + if(begin == end) + return; + BOOL substitute = FALSE; + BOOL* substitute_ptr = codepage == 65001 || codepage == 65000 ? 0 : &substitute; + char subst_char = 0; + char* subst_char_ptr = codepage == 65001 || codepage == 65000 ? 0 : &subst_char; + + const std::ptrdiff_t num_chars = end - begin; + if(num_chars > std::numeric_limits::max()) + throw conversion_error(); + int n = + WideCharToMultiByte(codepage, 0, begin, static_cast(num_chars), 0, 0, subst_char_ptr, substitute_ptr); + buf.resize(n); + + if(WideCharToMultiByte(codepage, + 0, + begin, + static_cast(num_chars), + &buf[0], + n, + subst_char_ptr, + substitute_ptr) + == 0) + throw conversion_error(); + if(substitute) { + if(do_skip) + remove_substitutions(buf); + else + throw conversion_error(); + } + } + + void wide_to_multibyte(int codepage, const wchar_t* begin, const wchar_t* end, bool do_skip, std::vector& buf) + { + if(begin == end) + return; + buf.reserve(end - begin); + const wchar_t* e = std::find(begin, end, L'\0'); + const wchar_t* b = begin; + for(;;) { + std::vector tmp; + wide_to_multibyte_non_zero(codepage, b, e, do_skip, tmp); + size_t osize = buf.size(); + buf.resize(osize + tmp.size()); + std::copy(tmp.begin(), tmp.end(), buf.begin() + osize); + if(e != end) { + buf.push_back('\0'); + b = e + 1; + e = std::find(b, end, L'0'); + } else + break; + } + } + + template + bool validate_utf16(const CharType* str, size_t len) + { + const CharType* begin = str; + const CharType* end = str + len; + while(begin != end) { + utf::code_point c = utf::utf_traits::template decode(begin, end); + if(c == utf::illegal || c == utf::incomplete) + return false; + } + return true; + } + + template + void clean_invalid_utf16(const CharType* str, size_t len, std::vector& out) + { + out.reserve(len); + for(size_t i = 0; i < len; i++) { + uint16_t c = static_cast(str[i]); + + if(0xD800 <= c && c <= 0xDBFF) { + i++; + if(i >= len) + return; + uint16_t c2 = static_cast(str[i]); + if(0xDC00 <= c2 && c2 <= 0xDFFF) { + out.push_back(static_cast(c)); + out.push_back(static_cast(c2)); + } + } else if(0xDC00 <= c && c <= 0xDFFF) + continue; + else + out.push_back(static_cast(c)); + } + } + + class wconv_between : public converter_between { + public: + wconv_between() : how_(skip), to_code_page_(-1), from_code_page_(-1) {} + bool open(const char* to_charset, const char* from_charset, method_type how) override + { + how_ = how; + to_code_page_ = util::encoding_to_windows_codepage(to_charset); + from_code_page_ = util::encoding_to_windows_codepage(from_charset); + if(to_code_page_ == -1 || from_code_page_ == -1) + return false; + return true; + } + std::string convert(const char* begin, const char* end) override + { + if(to_code_page_ == 65001 && from_code_page_ == 65001) + return utf_to_utf(begin, end, how_); + + std::string res; + + std::vector tmp; // buffer for mb2w + std::wstring tmps; // buffer for utf_to_utf + const wchar_t* wbegin = 0; + const wchar_t* wend = 0; + + if(from_code_page_ == 65001) { + tmps = utf_to_utf(begin, end, how_); + if(tmps.empty()) + return res; + wbegin = tmps.c_str(); + wend = wbegin + tmps.size(); + } else { + multibyte_to_wide(from_code_page_, begin, end, how_ == skip, tmp); + if(tmp.empty()) + return res; + wbegin = &tmp[0]; + wend = wbegin + tmp.size(); + } + + if(to_code_page_ == 65001) { + return utf_to_utf(wbegin, wend, how_); + } + + std::vector ctmp; + wide_to_multibyte(to_code_page_, wbegin, wend, how_ == skip, ctmp); + if(ctmp.empty()) + return res; + res.assign(&ctmp.front(), ctmp.size()); + return res; + } + + private: + method_type how_; + int to_code_page_; + int from_code_page_; + }; + + template + class wconv_to_utf; + + template + class wconv_from_utf; + + template<> + class wconv_to_utf : public converter_to_utf { + public: + bool open(const char* cs, method_type how) override { return cvt.open("UTF-8", cs, how); } + std::string convert(const char* begin, const char* end) override { return cvt.convert(begin, end); } + + private: + wconv_between cvt; + }; + + template<> + class wconv_from_utf : public converter_from_utf { + public: + bool open(const char* cs, method_type how) override { return cvt.open(cs, "UTF-8", how); } + std::string convert(const char* begin, const char* end) override { return cvt.convert(begin, end); } + + private: + wconv_between cvt; + }; + + template + class wconv_to_utf : public converter_to_utf { + public: + typedef CharType char_type; + + typedef std::basic_string string_type; + + wconv_to_utf() : how_(skip), code_page_(-1) {} + + bool open(const char* charset, method_type how) override + { + how_ = how; + code_page_ = util::encoding_to_windows_codepage(charset); + return code_page_ != -1; + } + + string_type convert(const char* begin, const char* end) override + { + if(code_page_ == 65001) { + return utf_to_utf(begin, end, how_); + } + std::vector tmp; + multibyte_to_wide(code_page_, begin, end, how_ == skip, tmp); + string_type res; + if(!tmp.empty()) + res.assign(reinterpret_cast(&tmp.front()), tmp.size()); + return res; + } + + private: + method_type how_; + int code_page_; + }; + + template + class wconv_from_utf : public converter_from_utf { + public: + typedef CharType char_type; + + typedef std::basic_string string_type; + + wconv_from_utf() : how_(skip), code_page_(-1) {} + + bool open(const char* charset, method_type how) override + { + how_ = how; + code_page_ = util::encoding_to_windows_codepage(charset); + return code_page_ != -1; + } + + std::string convert(const CharType* begin, const CharType* end) override + { + if(code_page_ == 65001) { + return utf_to_utf(begin, end, how_); + } + const wchar_t* wbegin = 0; + const wchar_t* wend = 0; + std::vector buffer; // if needed + if(begin == end) + return std::string(); + if(validate_utf16(begin, end - begin)) { + wbegin = reinterpret_cast(begin); + wend = reinterpret_cast(end); + } else { + if(how_ == stop) { + throw conversion_error(); + } else { + clean_invalid_utf16(begin, end - begin, buffer); + if(!buffer.empty()) { + wbegin = &buffer[0]; + wend = wbegin + buffer.size(); + } + } + } + std::string res; + if(wbegin == wend) + return res; + std::vector ctmp; + wide_to_multibyte(code_page_, wbegin, wend, how_ == skip, ctmp); + if(ctmp.empty()) + return res; + res.assign(&ctmp.front(), ctmp.size()); + return res; + } + + private: + method_type how_; + int code_page_; + }; + + template + class wconv_to_utf : public converter_to_utf { + public: + typedef CharType char_type; + + typedef std::basic_string string_type; + + wconv_to_utf() : how_(skip), code_page_(-1) {} + + bool open(const char* charset, method_type how) override + { + how_ = how; + code_page_ = util::encoding_to_windows_codepage(charset); + return code_page_ != -1; + } + + string_type convert(const char* begin, const char* end) override + { + if(code_page_ == 65001) { + return utf_to_utf(begin, end, how_); + } + std::vector buf; + multibyte_to_wide(code_page_, begin, end, how_ == skip, buf); + + if(buf.empty()) + return string_type(); + + return utf_to_utf(&buf[0], &buf[0] + buf.size(), how_); + } + + private: + method_type how_; + int code_page_; + }; + + template + class wconv_from_utf : public converter_from_utf { + public: + typedef CharType char_type; + + typedef std::basic_string string_type; + + wconv_from_utf() : how_(skip), code_page_(-1) {} + + bool open(const char* charset, method_type how) override + { + how_ = how; + code_page_ = util::encoding_to_windows_codepage(charset); + return code_page_ != -1; + } + + std::string convert(const CharType* begin, const CharType* end) override + { + if(code_page_ == 65001) { + return utf_to_utf(begin, end, how_); + } + std::wstring tmp = utf_to_utf(begin, end, how_); + + std::vector ctmp; + wide_to_multibyte(code_page_, tmp.c_str(), tmp.c_str() + tmp.size(), how_ == skip, ctmp); + std::string res; + if(ctmp.empty()) + return res; + res.assign(&ctmp.front(), ctmp.size()); + return res; + } + + private: + method_type how_; + int code_page_; + }; + +}}}} // namespace boost::locale::conv::impl + +// boostinspect:nominmax +#endif diff --git a/libs/locale/src/boost/locale/icu/all_generator.hpp b/libs/locale/src/boost/locale/icu/all_generator.hpp new file mode 100644 index 0000000..9c429a4 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/all_generator.hpp @@ -0,0 +1,24 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_ALL_GENERATOR_HPP +#define BOOST_LOCALE_IMPL_ALL_GENERATOR_HPP + +#include + +namespace boost { namespace locale { namespace impl_icu { + struct cdata; + std::locale create_convert(const std::locale&, const cdata&, char_facet_t); + std::locale create_collate(const std::locale&, const cdata&, char_facet_t); + std::locale create_formatting(const std::locale&, const cdata&, char_facet_t); + std::locale create_parsing(const std::locale&, const cdata&, char_facet_t); + std::locale create_codecvt(const std::locale&, const std::string& encoding, char_facet_t); + std::locale create_boundary(const std::locale&, const cdata&, char_facet_t); + std::locale create_calendar(const std::locale&, const cdata&); + +}}} // namespace boost::locale::impl_icu + +#endif diff --git a/libs/locale/src/boost/locale/icu/boundary.cpp b/libs/locale/src/boost/locale/icu/boundary.cpp new file mode 100644 index 0000000..a992048 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/boundary.cpp @@ -0,0 +1,213 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2021-2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include "boost/locale//util/encoding.hpp" +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/cdata.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/uconv.hpp" +#if BOOST_LOCALE_ICU_VERSION >= 306 +# include +#endif +#include +#include +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(disable : 4244) // 'argument' : conversion from 'int' +# pragma warning(disable : 4267) // 'argument' : conversion from 'size_t' +#endif + +namespace boost { namespace locale { + namespace boundary { namespace impl_icu { + + using namespace boost::locale::impl_icu; + + index_type map_direct(boundary_type t, icu::BreakIterator* it, int reserve) + { + index_type indx; + indx.reserve(reserve); +#if U_ICU_VERSION_MAJOR_NUM >= 52 + icu::BreakIterator* rbbi = it; +#else + icu::RuleBasedBreakIterator* rbbi = icu_cast(it); +#endif + + indx.push_back(break_info()); + it->first(); + int pos = 0; + while((pos = it->next()) != icu::BreakIterator::DONE) { + indx.push_back(break_info(pos)); + /// Character does not have any specific break types + if(t != character && rbbi) { + // + // There is a collapse for MSVC: int32_t defined by both boost::cstdint and icu... + // So need to pick one ;( + // + std::vector buffer; + int32_t membuf[8] = {0}; // try not to use memory allocation if possible + int32_t* buf = membuf; + + UErrorCode err = U_ZERO_ERROR; + int n = rbbi->getRuleStatusVec(buf, 8, err); + + if(err == U_BUFFER_OVERFLOW_ERROR) { + buf = &buffer.front(); + buffer.resize(n, 0); + n = rbbi->getRuleStatusVec(buf, buffer.size(), err); + } + + check_and_throw_icu_error(err); + + for(int i = 0; i < n; i++) { + switch(t) { + case word: + if(UBRK_WORD_NONE <= buf[i] && buf[i] < UBRK_WORD_NONE_LIMIT) + indx.back().rule |= word_none; + else if(UBRK_WORD_NUMBER <= buf[i] && buf[i] < UBRK_WORD_NUMBER_LIMIT) + indx.back().rule |= word_number; + else if(UBRK_WORD_LETTER <= buf[i] && buf[i] < UBRK_WORD_LETTER_LIMIT) + indx.back().rule |= word_letter; + else if(UBRK_WORD_KANA <= buf[i] && buf[i] < UBRK_WORD_KANA_LIMIT) + indx.back().rule |= word_kana; + else if(UBRK_WORD_IDEO <= buf[i] && buf[i] < UBRK_WORD_IDEO_LIMIT) + indx.back().rule |= word_ideo; + break; + + case line: + if(UBRK_LINE_SOFT <= buf[i] && buf[i] < UBRK_LINE_SOFT_LIMIT) + indx.back().rule |= line_soft; + else if(UBRK_LINE_HARD <= buf[i] && buf[i] < UBRK_LINE_HARD_LIMIT) + indx.back().rule |= line_hard; + break; + + case sentence: + if(UBRK_SENTENCE_TERM <= buf[i] && buf[i] < UBRK_SENTENCE_TERM_LIMIT) + indx.back().rule |= sentence_term; + else if(UBRK_SENTENCE_SEP <= buf[i] && buf[i] < UBRK_SENTENCE_SEP_LIMIT) + indx.back().rule |= sentence_sep; + break; + case character: BOOST_UNREACHABLE_RETURN(0); + } + } + } else { + indx.back().rule |= character_any; // Baisc mark... for character + } + } + return indx; + } + + std::unique_ptr get_iterator(boundary_type t, const icu::Locale& loc) + { + UErrorCode err = U_ZERO_ERROR; + std::unique_ptr bi; + switch(t) { + case character: bi.reset(icu::BreakIterator::createCharacterInstance(loc, err)); break; + case word: bi.reset(icu::BreakIterator::createWordInstance(loc, err)); break; + case sentence: bi.reset(icu::BreakIterator::createSentenceInstance(loc, err)); break; + case line: bi.reset(icu::BreakIterator::createLineInstance(loc, err)); break; + } + check_and_throw_icu_error(err); + if(!bi) + throw std::runtime_error("Failed to create break iterator"); + return bi; + } + + template + index_type do_map(boundary_type t, + const CharType* begin, + const CharType* end, + const icu::Locale& loc, + const std::string& encoding) + { + index_type indx; + std::unique_ptr bi = get_iterator(t, loc); + +#if BOOST_LOCALE_ICU_VERSION >= 306 + UErrorCode err = U_ZERO_ERROR; + BOOST_LOCALE_START_CONST_CONDITION + if(sizeof(CharType) == 2 || (sizeof(CharType) == 1 && util::normalize_encoding(encoding) == "utf8")) { + UText* ut = 0; + try { + if(sizeof(CharType) == 1) + ut = utext_openUTF8(0, reinterpret_cast(begin), end - begin, &err); + else // sizeof(CharType)==2 + ut = utext_openUChars(0, reinterpret_cast(begin), end - begin, &err); + BOOST_LOCALE_END_CONST_CONDITION + + check_and_throw_icu_error(err); + err = U_ZERO_ERROR; + if(!ut) + throw std::runtime_error("Failed to create UText"); + bi->setText(ut, err); + check_and_throw_icu_error(err); + index_type res = map_direct(t, bi.get(), end - begin); + indx.swap(res); + } catch(...) { + if(ut) + utext_close(ut); + throw; + } + if(ut) + utext_close(ut); + } else +#endif + { + icu_std_converter cvt(encoding); + icu::UnicodeString str = cvt.icu(begin, end); + bi->setText(str); + index_type indirect = map_direct(t, bi.get(), str.length()); + indx = indirect; + for(size_t i = 1; i < indirect.size(); i++) { + size_t offset_inderect = indirect[i - 1].offset; + size_t diff = indirect[i].offset - offset_inderect; + size_t offset_direct = indx[i - 1].offset; + indx[i].offset = offset_direct + cvt.cut(str, begin, end, diff, offset_inderect, offset_direct); + } + } + return indx; + } // do_map + + template + class boundary_indexing_impl : public boundary_indexing { + public: + boundary_indexing_impl(const cdata& data) : locale_(data.locale), encoding_(data.encoding) {} + index_type map(boundary_type t, const CharType* begin, const CharType* end) const + { + return do_map(t, begin, end, locale_, encoding_); + } + + private: + icu::Locale locale_; + std::string encoding_; + }; + + }} // namespace boundary::impl_icu + + namespace impl_icu { + std::locale create_boundary(const std::locale& in, const cdata& cd, char_facet_t type) + { + using namespace boost::locale::boundary::impl_icu; + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new boundary_indexing_impl(cd)); + case char_facet_t::wchar_f: return std::locale(in, new boundary_indexing_impl(cd)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new boundary_indexing_impl(cd)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new boundary_indexing_impl(cd)); +#endif + } + return in; + } + } // namespace impl_icu + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/icu/cdata.hpp b/libs/locale/src/boost/locale/icu/cdata.hpp new file mode 100644 index 0000000..851a265 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/cdata.hpp @@ -0,0 +1,22 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_ICU_CDATA_HPP +#define BOOST_LOCALE_ICU_CDATA_HPP + +#include +#include +#include + +namespace boost { namespace locale { namespace impl_icu { + struct cdata { + icu::Locale locale; + std::string encoding; + bool utf8; + }; +}}} // namespace boost::locale::impl_icu + +#endif diff --git a/libs/locale/src/boost/locale/icu/codecvt.cpp b/libs/locale/src/boost/locale/icu/codecvt.cpp new file mode 100644 index 0000000..b62a542 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/codecvt.cpp @@ -0,0 +1,129 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/icu/codecvt.hpp" +#include +#include +#include +#include +#include "boost/locale/encoding/conv.hpp" +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/uconv.hpp" +#include "boost/locale/util/encoding.hpp" +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(disable : 4244) // loose data +#endif + +namespace boost { namespace locale { namespace impl_icu { + class uconv_converter : public util::base_converter { + public: + uconv_converter(const std::string& encoding) : encoding_(encoding) + { + UErrorCode err = U_ZERO_ERROR; + + // No need to check err each time, this + // is how ICU works. + cvt_ = ucnv_open(encoding.c_str(), &err); + ucnv_setFromUCallBack(cvt_, UCNV_FROM_U_CALLBACK_STOP, 0, 0, 0, &err); + ucnv_setToUCallBack(cvt_, UCNV_TO_U_CALLBACK_STOP, 0, 0, 0, &err); + + if(!cvt_ || U_FAILURE(err)) { + if(cvt_) + ucnv_close(cvt_); + throw conv::invalid_charset_error(encoding); + } + + max_len_ = ucnv_getMaxCharSize(cvt_); + } + + ~uconv_converter() { ucnv_close(cvt_); } + + bool is_thread_safe() const override { return false; } + + uconv_converter* clone() const override { return new uconv_converter(encoding_); } + + uint32_t to_unicode(const char*& begin, const char* end) override + { + UErrorCode err = U_ZERO_ERROR; + const char* tmp = begin; + UChar32 c = ucnv_getNextUChar(cvt_, &tmp, end, &err); + ucnv_reset(cvt_); + if(err == U_TRUNCATED_CHAR_FOUND) { + return incomplete; + } + if(U_FAILURE(err)) { + return illegal; + } + + begin = tmp; + return c; + } + + uint32_t from_unicode(uint32_t u, char* begin, const char* end) override + { + UChar code_point[2] = {0}; + int len; + if(u <= 0xFFFF) { + if(0xD800 <= u && u <= 0xDFFF) // No surrogates + return illegal; + code_point[0] = u; + len = 1; + } else { + u -= 0x10000; + code_point[0] = 0xD800 | (u >> 10); + code_point[1] = 0xDC00 | (u & 0x3FF); + len = 2; + } + UErrorCode err = U_ZERO_ERROR; + int olen = ucnv_fromUChars(cvt_, begin, end - begin, code_point, len, &err); + ucnv_reset(cvt_); + if(err == U_BUFFER_OVERFLOW_ERROR) + return incomplete; + if(U_FAILURE(err)) + return illegal; + return olen; + } + + int max_len() const override { return max_len_; } + + private: + std::string encoding_; + UConverter* cvt_; + int max_len_; + }; + + std::unique_ptr create_uconv_converter(const std::string& encoding) + { + try { + return std::unique_ptr(new uconv_converter(encoding)); + } catch(const std::exception& /*e*/) { + return nullptr; + } + } + + std::locale create_codecvt(const std::locale& in, const std::string& encoding, char_facet_t type) + { + if(util::normalize_encoding(encoding) == "utf8") + return util::create_utf8_codecvt(in, type); + + try { + return util::create_simple_codecvt(in, encoding, type); + } catch(const boost::locale::conv::invalid_charset_error&) { + std::unique_ptr cvt; + try { + cvt = create_uconv_converter(encoding); + } catch(const std::exception& /*e*/) { + cvt.reset(new util::base_converter()); + } + return util::create_codecvt(in, std::move(cvt), type); + } + } + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/codecvt.hpp b/libs/locale/src/boost/locale/icu/codecvt.hpp new file mode 100644 index 0000000..f406f7f --- /dev/null +++ b/libs/locale/src/boost/locale/icu/codecvt.hpp @@ -0,0 +1,20 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_ICU_CODECVT_HPP +#define BOOST_LOCALE_IMPL_ICU_CODECVT_HPP + +#include +#include +#include + +namespace boost { namespace locale { namespace impl_icu { + BOOST_LOCALE_DECL + std::unique_ptr create_uconv_converter(const std::string& encoding); + +}}} // namespace boost::locale::impl_icu + +#endif diff --git a/libs/locale/src/boost/locale/icu/collator.cpp b/libs/locale/src/boost/locale/icu/collator.cpp new file mode 100644 index 0000000..c199ba9 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/collator.cpp @@ -0,0 +1,193 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/cdata.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/uconv.hpp" +#include "boost/locale/shared/mo_hash.hpp" +#include +#include +#include +#include +#if BOOST_LOCALE_ICU_VERSION >= 402 +# define BOOST_LOCALE_WITH_STRINGPIECE 1 +# include +#else +# define BOOST_LOCALE_WITH_STRINGPIECE 0 +#endif + +#ifdef BOOST_MSVC +# pragma warning(disable : 4244) // 'argument' : conversion from 'int' +# pragma warning(disable : 4267) // 'argument' : conversion from 'size_t' +#endif + +namespace boost { namespace locale { namespace impl_icu { + template + class collate_impl : public collator { + public: + int level_to_int(collate_level level) const + { + const auto res = static_cast(level); + if(res < 0) + return 0; + if(res >= level_count) + return level_count - 1; + return res; + } + +#if BOOST_LOCALE_WITH_STRINGPIECE + int do_utf8_compare(collate_level level, + const char* b1, + const char* e1, + const char* b2, + const char* e2, + UErrorCode& status) const + { + icu::StringPiece left(b1, e1 - b1); + icu::StringPiece right(b2, e2 - b2); + return get_collator(level)->compareUTF8(left, right, status); + } +#endif + + int do_ustring_compare(collate_level level, + const CharType* b1, + const CharType* e1, + const CharType* b2, + const CharType* e2, + UErrorCode& status) const + { + icu::UnicodeString left = cvt_.icu(b1, e1); + icu::UnicodeString right = cvt_.icu(b2, e2); + return get_collator(level)->compare(left, right, status); + } + + int do_real_compare(collate_level level, + const CharType* b1, + const CharType* e1, + const CharType* b2, + const CharType* e2, + UErrorCode& status) const + { + return do_ustring_compare(level, b1, e1, b2, e2, status); + } + + int do_compare(collate_level level, + const CharType* b1, + const CharType* e1, + const CharType* b2, + const CharType* e2) const override + { + UErrorCode status = U_ZERO_ERROR; + + int res = do_real_compare(level, b1, e1, b2, e2, status); + + if(U_FAILURE(status)) + throw std::runtime_error(std::string("Collation failed:") + u_errorName(status)); + if(res < 0) + return -1; + else if(res > 0) + return 1; + return 0; + } + + std::vector do_basic_transform(collate_level level, const CharType* b, const CharType* e) const + { + icu::UnicodeString str = cvt_.icu(b, e); + std::vector tmp; + tmp.resize(str.length() + 1u); + icu::Collator* collate = get_collator(level); + const int len = collate->getSortKey(str, &tmp[0], tmp.size()); + if(len > int(tmp.size())) { + tmp.resize(len); + collate->getSortKey(str, &tmp[0], tmp.size()); + } else + tmp.resize(len); + return tmp; + } + std::basic_string + do_transform(collate_level level, const CharType* b, const CharType* e) const override + { + std::vector tmp = do_basic_transform(level, b, e); + return std::basic_string(tmp.begin(), tmp.end()); + } + + long do_hash(collate_level level, const CharType* b, const CharType* e) const override + { + std::vector tmp = do_basic_transform(level, b, e); + tmp.push_back(0); + return gnu_gettext::pj_winberger_hash_function(reinterpret_cast(&tmp.front())); + } + + collate_impl(const cdata& d) : cvt_(d.encoding), locale_(d.locale), is_utf8_(d.utf8) {} + + icu::Collator* get_collator(collate_level level) const + { + const int lvl_idx = level_to_int(level); + constexpr icu::Collator::ECollationStrength levels[level_count] = {icu::Collator::PRIMARY, + icu::Collator::SECONDARY, + icu::Collator::TERTIARY, + icu::Collator::QUATERNARY, + icu::Collator::IDENTICAL}; + + icu::Collator* col = collates_[lvl_idx].get(); + if(col) + return col; + + UErrorCode status = U_ZERO_ERROR; + + collates_[lvl_idx].reset(icu::Collator::createInstance(locale_, status)); + + if(U_FAILURE(status)) + throw std::runtime_error(std::string("Creation of collate failed:") + u_errorName(status)); + + collates_[lvl_idx]->setStrength(levels[lvl_idx]); + return collates_[lvl_idx].get(); + } + + private: + static constexpr int level_count = static_cast(collate_level::identical) + 1; + icu_std_converter cvt_; + icu::Locale locale_; + mutable boost::thread_specific_ptr collates_[level_count]; + bool is_utf8_; + }; + +#if BOOST_LOCALE_WITH_STRINGPIECE + template<> + int collate_impl::do_real_compare(collate_level level, + const char* b1, + const char* e1, + const char* b2, + const char* e2, + UErrorCode& status) const + { + if(is_utf8_) + return do_utf8_compare(level, b1, e1, b2, e2, status); + else + return do_ustring_compare(level, b1, e1, b2, e2, status); + } +#endif + + std::locale create_collate(const std::locale& in, const cdata& cd, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new collate_impl(cd)); + case char_facet_t::wchar_f: return std::locale(in, new collate_impl(cd)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new collate_impl(cd)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new collate_impl(cd)); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/conversion.cpp b/libs/locale/src/boost/locale/icu/conversion.cpp new file mode 100644 index 0000000..89609c1 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/conversion.cpp @@ -0,0 +1,186 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/cdata.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/uconv.hpp" +#include +#include +#include +#include +#if BOOST_LOCALE_ICU_VERSION >= 308 +# include +# define BOOST_LOCALE_WITH_CASEMAP +#endif +#include + +namespace boost { namespace locale { namespace impl_icu { + + namespace { + void normalize_string(icu::UnicodeString& str, int flags) + { + UErrorCode code = U_ZERO_ERROR; + UNormalizationMode mode = UNORM_DEFAULT; + switch(flags) { + case norm_nfd: mode = UNORM_NFD; break; + case norm_nfc: mode = UNORM_NFC; break; + case norm_nfkd: mode = UNORM_NFKD; break; + case norm_nfkc: mode = UNORM_NFKC; break; + } + icu::UnicodeString tmp; + icu::Normalizer::normalize(str, mode, 0, tmp, code); + + check_and_throw_icu_error(code); + + str = tmp; + } + } // namespace + + template + class converter_impl : public converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + converter_impl(const cdata& d) : locale_(d.locale), encoding_(d.encoding) {} + + string_type convert(converter_base::conversion_type how, + const char_type* begin, + const char_type* end, + int flags = 0) const override + { + icu_std_converter cvt(encoding_); + icu::UnicodeString str = cvt.icu(begin, end); + switch(how) { + case converter_base::normalization: normalize_string(str, flags); break; + case converter_base::upper_case: str.toUpper(locale_); break; + case converter_base::lower_case: str.toLower(locale_); break; + case converter_base::title_case: str.toTitle(0, locale_); break; + case converter_base::case_folding: str.foldCase(); break; + } + return cvt.std(str); + } + + private: + icu::Locale locale_; + std::string encoding_; + }; // converter_impl + +#ifdef BOOST_LOCALE_WITH_CASEMAP + template + struct get_casemap_size_type; + + template + struct get_casemap_size_type { + using type = TSize; + }; + + class raii_casemap { + public: + raii_casemap(const raii_casemap&) = delete; + void operator=(const raii_casemap&) = delete; + + raii_casemap(const std::string& locale_id) : map_(0) + { + UErrorCode err = U_ZERO_ERROR; + map_ = ucasemap_open(locale_id.c_str(), 0, &err); + check_and_throw_icu_error(err); + if(!map_) + throw std::runtime_error("Failed to create UCaseMap"); + } + template + std::string convert(Conv func, const char* begin, const char* end) const + { + using size_type = typename get_casemap_size_type::type; + if((end - begin) >= std::numeric_limits::max() / 11) + throw std::range_error("String to long to be converted by ICU"); + const auto max_converted_size = (end - begin) * 11 / 10 + 1; + if(max_converted_size >= std::numeric_limits::max()) + throw std::range_error("String to long to be converted by ICU"); + std::vector buf(max_converted_size); + UErrorCode err = U_ZERO_ERROR; + auto size = func(map_, + &buf.front(), + static_cast(buf.size()), + begin, + static_cast(end - begin), + &err); + if(err == U_BUFFER_OVERFLOW_ERROR) { + err = U_ZERO_ERROR; + buf.resize(size + 1); + size = func(map_, + &buf.front(), + static_cast(buf.size()), + begin, + static_cast(end - begin), + &err); + } + check_and_throw_icu_error(err); + return std::string(&buf.front(), size); + } + ~raii_casemap() { ucasemap_close(map_); } + + private: + UCaseMap* map_; + }; + + class utf8_converter_impl : public converter { + public: + utf8_converter_impl(const cdata& d) : locale_id_(d.locale.getName()), map_(locale_id_) {} + + std::string + convert(converter_base::conversion_type how, const char* begin, const char* end, int flags = 0) const override + { + switch(how) { + case converter_base::upper_case: return map_.convert(ucasemap_utf8ToUpper, begin, end); + case converter_base::lower_case: return map_.convert(ucasemap_utf8ToLower, begin, end); + case converter_base::title_case: { + // Non-const method, so need to create a separate map + raii_casemap map(locale_id_); + return map.convert(ucasemap_utf8ToTitle, begin, end); + } + case converter_base::case_folding: return map_.convert(ucasemap_utf8FoldCase, begin, end); + case converter_base::normalization: { + icu_std_converter cvt("UTF-8"); + icu::UnicodeString str = cvt.icu(begin, end); + normalize_string(str, flags); + return cvt.std(str); + } + } + return std::string(begin, end - begin); + } + + private: + std::string locale_id_; + raii_casemap map_; + }; // converter_impl + +#endif // BOOST_LOCALE_WITH_CASEMAP + + std::locale create_convert(const std::locale& in, const cdata& cd, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: +#ifdef BOOST_LOCALE_WITH_CASEMAP + if(cd.utf8) + return std::locale(in, new utf8_converter_impl(cd)); +#endif + return std::locale(in, new converter_impl(cd)); + case char_facet_t::wchar_f: return std::locale(in, new converter_impl(cd)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new converter_impl(cd)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new converter_impl(cd)); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/date_time.cpp b/libs/locale/src/boost/locale/icu/date_time.cpp new file mode 100644 index 0000000..450e7ea --- /dev/null +++ b/libs/locale/src/boost/locale/icu/date_time.cpp @@ -0,0 +1,234 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2021-2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/cdata.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/time_zone.hpp" +#include "boost/locale/icu/uconv.hpp" +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace impl_icu { + + static void check_and_throw_dt(UErrorCode& e) + { + if(U_FAILURE(e)) { + throw date_time_error(u_errorName(e)); + } + } + using period::marks::period_mark; + + static UCalendarDateFields to_icu(period::marks::period_mark f) + { + using namespace period::marks; + + switch(f) { + case era: return UCAL_ERA; + case year: return UCAL_YEAR; + case extended_year: return UCAL_EXTENDED_YEAR; + case month: return UCAL_MONTH; + case day: return UCAL_DATE; + case day_of_year: return UCAL_DAY_OF_YEAR; + case day_of_week: return UCAL_DAY_OF_WEEK; + case day_of_week_in_month: return UCAL_DAY_OF_WEEK_IN_MONTH; + case day_of_week_local: return UCAL_DOW_LOCAL; + case hour: return UCAL_HOUR_OF_DAY; + case hour_12: return UCAL_HOUR; + case am_pm: return UCAL_AM_PM; + case minute: return UCAL_MINUTE; + case second: return UCAL_SECOND; + case week_of_year: return UCAL_WEEK_OF_YEAR; + case week_of_month: return UCAL_WEEK_OF_MONTH; + case first_day_of_week: + case invalid: break; + } + throw std::invalid_argument("Invalid date_time period type"); + } + + class calendar_impl : public abstract_calendar { + public: + calendar_impl(const cdata& dat) + { + UErrorCode err = U_ZERO_ERROR; + calendar_.reset(icu::Calendar::createInstance(dat.locale, err)); + check_and_throw_dt(err); +#if BOOST_LOCALE_ICU_VERSION < 402 + // workaround old/invalid data, it should be 4 in general + calendar_->setMinimalDaysInFirstWeek(4); +#endif + encoding_ = dat.encoding; + } + calendar_impl(const calendar_impl& other) + { + calendar_.reset(other.calendar_->clone()); + encoding_ = other.encoding_; + } + + calendar_impl* clone() const override + { + return new calendar_impl(*this); + } + + void set_value(period::marks::period_mark p, int value) override + { + calendar_->set(to_icu(p), int32_t(value)); + } + + int get_value(period::marks::period_mark p, value_type type) const override + { + UErrorCode err = U_ZERO_ERROR; + int v = 0; + if(p == period::marks::first_day_of_week) { + guard l(lock_); + v = calendar_->getFirstDayOfWeek(err); + } else { + UCalendarDateFields uper = to_icu(p); + guard l(lock_); + switch(type) { + case absolute_minimum: v = calendar_->getMinimum(uper); break; + case actual_minimum: v = calendar_->getActualMinimum(uper, err); break; + case greatest_minimum: v = calendar_->getGreatestMinimum(uper); break; + case current: v = calendar_->get(uper, err); break; + case least_maximum: v = calendar_->getLeastMaximum(uper); break; + case actual_maximum: v = calendar_->getActualMaximum(uper, err); break; + case absolute_maximum: v = calendar_->getMaximum(uper); break; + } + } + check_and_throw_dt(err); + return v; + } + + void set_time(const posix_time& p) override + { + double utime = p.seconds * 1000.0 + p.nanoseconds / 1000000.0; + UErrorCode code = U_ZERO_ERROR; + calendar_->setTime(utime, code); + check_and_throw_dt(code); + } + void normalize() override + { + // Can't call complete() explicitly (protected) + // calling get which calls complete + UErrorCode code = U_ZERO_ERROR; + calendar_->get(UCAL_YEAR, code); + check_and_throw_dt(code); + } + posix_time get_time() const override + { + const double timeMs = get_time_ms(); + posix_time res; + res.seconds = static_cast(std::floor(timeMs / 1e3)); + const double remainTimeMs = std::fmod(timeMs, 1e3); // = timeMs - seconds * 1000 + constexpr uint32_t ns_in_s = static_cast(1000) * 1000 * 1000; + res.nanoseconds = std::min(static_cast(remainTimeMs * 1e6), ns_in_s - 1u); + return res; + } + double get_time_ms() const override + { + UErrorCode code = U_ZERO_ERROR; + double result; + { + guard l(lock_); + result = calendar_->getTime(code); + } + check_and_throw_dt(code); + return result; + } + void set_option(calendar_option_type opt, int /*v*/) override + { + switch(opt) { + case is_gregorian: throw date_time_error("is_gregorian is not settable options for calendar"); + case is_dst: throw date_time_error("is_dst is not settable options for calendar"); + } + } + int get_option(calendar_option_type opt) const override + { + switch(opt) { + case is_gregorian: return icu_cast(calendar_.get()) != 0; + case is_dst: { + guard l(lock_); + UErrorCode err = U_ZERO_ERROR; + bool res = (calendar_->inDaylightTime(err) != 0); + check_and_throw_dt(err); + return res; + } + } + return 0; + } + void adjust_value(period::marks::period_mark p, update_type u, int difference) override + { + UErrorCode err = U_ZERO_ERROR; + switch(u) { + case move: calendar_->add(to_icu(p), difference, err); break; + case roll: calendar_->roll(to_icu(p), difference, err); break; + } + check_and_throw_dt(err); + } + int difference(const abstract_calendar& other, period::marks::period_mark m) const override + { + UErrorCode err = U_ZERO_ERROR; + const double other_time_ms = other.get_time_ms(); + + // fieldDifference has side effect of moving calendar (WTF?) + // So we clone it for performing this operation + hold_ptr self(calendar_->clone()); + + int diff = self->fieldDifference(other_time_ms, to_icu(m), err); + + check_and_throw_dt(err); + return diff; + } + void set_timezone(const std::string& tz) override + { + calendar_->adoptTimeZone(get_time_zone(tz)); + } + std::string get_timezone() const override + { + icu::UnicodeString tz; + calendar_->getTimeZone().getID(tz); + icu_std_converter cvt(encoding_); + return cvt.std(tz); + } + bool same(const abstract_calendar* other) const override + { + const calendar_impl* oc = dynamic_cast(other); + if(!oc) + return false; + return calendar_->isEquivalentTo(*oc->calendar_) != 0; + } + + private: + typedef boost::unique_lock guard; + mutable boost::mutex lock_; + std::string encoding_; + hold_ptr calendar_; + }; + + class icu_calendar_facet : public calendar_facet { + public: + icu_calendar_facet(const cdata& d, size_t refs = 0) : calendar_facet(refs), data_(d) {} + abstract_calendar* create_calendar() const override { return new calendar_impl(data_); } + + private: + cdata data_; + }; + + std::locale create_calendar(const std::locale& in, const cdata& d) + { + return std::locale(in, new icu_calendar_facet(d)); + } + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/formatter.cpp b/libs/locale/src/boost/locale/icu/formatter.cpp new file mode 100644 index 0000000..6167852 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/formatter.cpp @@ -0,0 +1,459 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2021-2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/icu/formatter.hpp" +#include +#include +#include "boost/locale/icu/formatters_cache.hpp" +#include "boost/locale/icu/icu_util.hpp" +#include "boost/locale/icu/time_zone.hpp" +#include "boost/locale/icu/uconv.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(disable : 4244) // lose data +#endif + +namespace boost { namespace locale { namespace impl_icu { + + namespace { + // Set the min/max fraction digits for the NumberFormat + void set_fraction_digits(icu::NumberFormat& nf, const std::ios_base::fmtflags how, std::streamsize precision) + { +#if BOOST_LOCALE_ICU_VERSION >= 5601 + // Since ICU 56.1 the integer part counts to the fraction part + if(how == std::ios_base::scientific) + precision += nf.getMaximumIntegerDigits(); +#endif + nf.setMaximumFractionDigits(precision); + if(how == std::ios_base::scientific || how == std::ios_base::fixed) { + nf.setMinimumFractionDigits(precision); + } else { + nf.setMinimumFractionDigits(0); + } + } + } // namespace + + template + class number_format : public formatter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + number_format(icu::NumberFormat& fmt, std::string codepage) : cvt_(codepage), icu_fmt_(fmt) {} + + string_type format(double value, size_t& code_points) const override { return do_format(value, code_points); } + string_type format(int64_t value, size_t& code_points) const override { return do_format(value, code_points); } + string_type format(int32_t value, size_t& code_points) const override { return do_format(value, code_points); } + size_t parse(const string_type& str, double& value) const override { return do_parse(str, value); } + size_t parse(const string_type& str, int64_t& value) const override { return do_parse(str, value); } + size_t parse(const string_type& str, int32_t& value) const override { return do_parse(str, value); } + + private: + bool get_value(double& v, icu::Formattable& fmt) const + { + UErrorCode err = U_ZERO_ERROR; + v = fmt.getDouble(err); + if(U_FAILURE(err)) + return false; + return true; + } + + bool get_value(int64_t& v, icu::Formattable& fmt) const + { + UErrorCode err = U_ZERO_ERROR; + v = fmt.getInt64(err); + if(U_FAILURE(err)) + return false; + return true; + } + + bool get_value(int32_t& v, icu::Formattable& fmt) const + { + UErrorCode err = U_ZERO_ERROR; + v = fmt.getLong(err); + if(U_FAILURE(err)) + return false; + return true; + } + + template + string_type do_format(ValueType value, size_t& code_points) const + { + icu::UnicodeString tmp; + icu_fmt_.format(value, tmp); + code_points = tmp.countChar32(); + return cvt_.std(tmp); + } + + template + size_t do_parse(const string_type& str, ValueType& v) const + { + icu::Formattable val; + icu::ParsePosition pp; + icu::UnicodeString tmp = cvt_.icu(str.data(), str.data() + str.size()); + + icu_fmt_.parse(tmp, val, pp); + + ValueType tmp_v; + + if(pp.getIndex() == 0 || !get_value(tmp_v, val)) + return 0; + size_t cut = cvt_.cut(tmp, str.data(), str.data() + str.size(), pp.getIndex()); + if(cut == 0) + return 0; + v = tmp_v; + return cut; + } + + icu_std_converter cvt_; + icu::NumberFormat& icu_fmt_; + }; + + template + class date_format : public formatter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + string_type format(double value, size_t& code_points) const override { return do_format(value, code_points); } + string_type format(int64_t value, size_t& code_points) const override { return do_format(value, code_points); } + + string_type format(int32_t value, size_t& code_points) const override { return do_format(value, code_points); } + + size_t parse(const string_type& str, double& value) const override { return do_parse(str, value); } + size_t parse(const string_type& str, int64_t& value) const override { return do_parse(str, value); } + size_t parse(const string_type& str, int32_t& value) const override { return do_parse(str, value); } + + date_format(icu::DateFormat& fmt, const std::string& encoding) : cvt_(encoding), icu_fmt_(fmt) {} + date_format(std::unique_ptr fmt, const std::string& encoding) : + cvt_(encoding), icu_fmt_holder_(std::move(fmt)), icu_fmt_(*icu_fmt_holder_) + {} + + private: + template + size_t do_parse(const string_type& str, ValueType& value) const + { + icu::ParsePosition pp; + icu::UnicodeString tmp = cvt_.icu(str.data(), str.data() + str.size()); + + UDate udate = icu_fmt_.parse(tmp, pp); + if(pp.getIndex() == 0) + return 0; + double date = udate / 1000.0; + typedef std::numeric_limits limits_type; + // Explicit cast to double to avoid warnings changing value (e.g. for INT64_MAX -> double) + if(date > static_cast(limits_type::max()) || date < static_cast(limits_type::min())) + return 0; + size_t cut = cvt_.cut(tmp, str.data(), str.data() + str.size(), pp.getIndex()); + if(cut == 0) + return 0; + // Handle the edge case where the double is slightly out of range and hence the cast would be UB + // by rounding to the min/max values + if(date == static_cast(limits_type::max())) + value = limits_type::max(); + else if(date == static_cast(limits_type::min())) + value = limits_type::min(); + else + value = static_cast(date); + return cut; + } + + string_type do_format(double value, size_t& codepoints) const + { + UDate date = value * 1000.0; /// UDate is time_t in milliseconds + icu::UnicodeString tmp; + icu_fmt_.format(date, tmp); + codepoints = tmp.countChar32(); + return cvt_.std(tmp); + } + + icu_std_converter cvt_; + std::unique_ptr icu_fmt_holder_; + icu::DateFormat& icu_fmt_; + }; + + icu::UnicodeString strftime_symbol_to_icu(const char c, const formatters_cache& cache) + { + switch(c) { + case 'a': // Abbr Weekday + return "EE"; + case 'A': // Full Weekday + return "EEEE"; + case 'b': // Abbr Month + return "MMM"; + case 'B': // Full Month + return "MMMM"; + case 'c': // DateTime + return cache.default_date_time_format(); + // not supported by ICU ;( + // case 'C': // Century -> 1980 -> 19 + case 'd': // Day of Month [01,31] + return "dd"; + case 'D': // %m/%d/%y + return "MM/dd/yy"; + case 'e': // Day of Month [1,31] + return "d"; + case 'h': // == b + return "MMM"; + case 'H': // 24 clock hour 00,23 + return "HH"; + case 'I': // 12 clock hour 01,12 + return "hh"; + case 'j': // day of year 001,366 + return "D"; + case 'm': // month as [01,12] + return "MM"; + case 'M': // minute [00,59] + return "mm"; + case 'n': // \n + return "\n"; + case 'p': // am-pm + return "a"; + case 'r': // time with AM/PM %I:%M:%S %p + return "hh:mm:ss a"; + case 'R': // %H:%M + return "HH:mm"; + case 'S': // second [00,61] + return "ss"; + case 't': // \t + return "\t"; + case 'T': // %H:%M:%S + return "HH:mm:ss"; + /* case 'u': // weekday 1,7 1=Monday + case 'U': // week number of year [00,53] Sunday first + case 'V': // week number of year [01,53] Monday first + case 'w': // weekday 0,7 0=Sunday + case 'W': // week number of year [00,53] Monday first, */ + case 'x': // Date + return cache.default_date_format(); + case 'X': // Time + return cache.default_time_format(); + case 'y': // Year [00-99] + return "yy"; + case 'Y': // Year 1998 + return "yyyy"; + case 'Z': // timezone + return "vvvv"; + case '%': // % + return "%"; + default: return ""; + } + } + + icu::UnicodeString strftime_to_icu(const icu::UnicodeString& ftime, const formatters_cache& cache) + { + const unsigned len = ftime.length(); + icu::UnicodeString result; + bool escaped = false; + for(unsigned i = 0; i < len; i++) { + UChar c = ftime[i]; + if(c == '%') { + i++; + c = ftime[i]; + if(c == 'E' || c == 'O') { + i++; + c = ftime[i]; + } + if(escaped) { + result += "'"; + escaped = false; + } + result += strftime_symbol_to_icu(c, cache); + } else if(c == '\'') { + result += "''"; + } else { + if(!escaped) { + result += "'"; + escaped = true; + } + result += c; + } + } + if(escaped) + result += "'"; + return result; + } + + format_len time_flags_to_len(const uint64_t time_flags) + { + switch(time_flags) { + using namespace boost::locale::flags; + case time_short: return format_len::Short; + case time_medium: return format_len::Medium; + case time_long: return format_len::Long; + case time_full: return format_len::Full; + default: return format_len::Medium; + } + } + format_len date_flags_to_len(const uint64_t date_flags) + { + switch(date_flags) { + using namespace boost::locale::flags; + case date_short: return format_len::Short; + case date_medium: return format_len::Medium; + case date_long: return format_len::Long; + case date_full: return format_len::Full; + default: return format_len::Medium; + } + } + icu::DateFormat::EStyle time_flags_to_icu_len(const uint64_t time_flags) + { + switch(time_flags) { + using namespace boost::locale::flags; + case time_short: return icu::DateFormat::kShort; + case time_medium: return icu::DateFormat::kMedium; + case time_long: return icu::DateFormat::kLong; + case time_full: return icu::DateFormat::kFull; + case time_default: + default: return icu::DateFormat::kDefault; + } + } + icu::DateFormat::EStyle date_flags_to_icu_len(const uint64_t date_flags) + { + switch(date_flags) { + using namespace boost::locale::flags; + case date_short: return icu::DateFormat::kShort; + case date_medium: return icu::DateFormat::kMedium; + case date_long: return icu::DateFormat::kLong; + case date_full: return icu::DateFormat::kFull; + case date_default: + default: return icu::DateFormat::kDefault; + } + } + + template + std::unique_ptr> + formatter::create(std::ios_base& ios, const icu::Locale& locale, const std::string& encoding) + { + using ptr_type = std::unique_ptr>; + + const ios_info& info = ios_info::get(ios); + const formatters_cache& cache = std::use_facet(ios.getloc()); + + const uint64_t disp = info.display_flags(); + switch(disp) { + using namespace boost::locale::flags; + case posix: + BOOST_ASSERT_MSG(false, "Shouldn't try to create a posix formatter"); // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + case number: { + const std::ios_base::fmtflags how = (ios.flags() & std::ios_base::floatfield); + icu::NumberFormat& nf = + cache.number_format((how == std::ios_base::scientific) ? num_fmt_type::sci : num_fmt_type::number); + set_fraction_digits(nf, how, ios.precision()); + return ptr_type(new number_format(nf, encoding)); + } + case currency: { + icu::NumberFormat& nf = cache.number_format( + (info.currency_flags() == currency_iso) ? num_fmt_type::curr_iso : num_fmt_type::curr_nat); + return ptr_type(new number_format(nf, encoding)); + } + case percent: { + icu::NumberFormat& nf = cache.number_format(num_fmt_type::percent); + set_fraction_digits(nf, ios.flags() & std::ios_base::floatfield, ios.precision()); + return ptr_type(new number_format(nf, encoding)); + } + case spellout: + return ptr_type(new number_format(cache.number_format(num_fmt_type::spell), encoding)); + case ordinal: + return ptr_type(new number_format(cache.number_format(num_fmt_type::ordinal), encoding)); + case date: + case time: + case datetime: + case strftime: { + using namespace flags; + std::unique_ptr new_df; + icu::DateFormat* df = 0; + // try to use cached first + { + icu::SimpleDateFormat* sdf = cache.date_formatter(); + if(sdf) { + icu::UnicodeString pattern; + switch(disp) { + case date: pattern = cache.date_format(date_flags_to_len(info.date_flags())); break; + case time: pattern = cache.time_format(time_flags_to_len(info.time_flags())); break; + case datetime: + pattern = cache.date_time_format(date_flags_to_len(info.date_flags()), + time_flags_to_len(info.time_flags())); + break; + case strftime: { + icu_std_converter cvt_(encoding); + const std::basic_string& f = info.date_time_pattern(); + pattern = strftime_to_icu(cvt_.icu(f.c_str(), f.c_str() + f.size()), locale); + } break; + } + if(!pattern.isEmpty()) { + sdf->applyPattern(pattern); + df = sdf; + } + } + } + + if(!df) { + switch(disp) { + case date: + new_df.reset( + icu::DateFormat::createDateInstance(date_flags_to_icu_len(info.date_flags()), locale)); + break; + case time: + new_df.reset( + icu::DateFormat::createTimeInstance(time_flags_to_icu_len(info.time_flags()), locale)); + break; + case datetime: + new_df.reset( + icu::DateFormat::createDateTimeInstance(date_flags_to_icu_len(info.date_flags()), + time_flags_to_icu_len(info.time_flags()), + locale)); + break; + case strftime: { + icu_std_converter cvt_(encoding); + const std::basic_string& f = info.date_time_pattern(); + icu::UnicodeString pattern = + strftime_to_icu(cvt_.icu(f.data(), f.data() + f.size()), locale); + UErrorCode err = U_ZERO_ERROR; + new_df.reset(new icu::SimpleDateFormat(pattern, locale, err)); + if(U_FAILURE(err)) + return nullptr; + } break; + } + df = new_df.get(); + BOOST_ASSERT_MSG(df, "Failed to create date/time formatter"); + } + + df->adoptTimeZone(get_time_zone(info.time_zone())); + + // Depending if we own formatter or not + if(new_df) + return ptr_type(new date_format(std::move(new_df), encoding)); + else + return ptr_type(new date_format(*df, encoding)); + } break; + } + + return nullptr; // LCOV_EXCL_LINE + } + + template class formatter; + template class formatter; + +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + template class formatter; +#endif + +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + template class formatter; +#endif +}}} // namespace boost::locale::impl_icu + +// boostinspect:nominmax diff --git a/libs/locale/src/boost/locale/icu/formatter.hpp b/libs/locale/src/boost/locale/icu/formatter.hpp new file mode 100644 index 0000000..cc55e76 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/formatter.hpp @@ -0,0 +1,73 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_FORMATTER_HPP_INCLUDED +#define BOOST_LOCALE_FORMATTER_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace impl_icu { + + /// \brief Special base polymorphic class that is used as a character type independent base for all formatter + /// classes + class base_formatter { + public: + virtual ~base_formatter() = default; + }; + + /// \brief A class that is used for formatting numbers, currency and dates/times + template + class formatter : public base_formatter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + /// Format the value and return the number of Unicode code points + virtual string_type format(double value, size_t& code_points) const = 0; + /// Format the value and return the number of Unicode code points + virtual string_type format(int64_t value, size_t& code_points) const = 0; + /// Format the value and return the number of Unicode code points + virtual string_type format(int32_t value, size_t& code_points) const = 0; + + /// Parse the string and return the number of used characters. If it returns 0 + /// then parsing failed. + virtual size_t parse(const string_type& str, double& value) const = 0; + /// Parse the string and return the number of used characters. If it returns 0 + /// then parsing failed. + virtual size_t parse(const string_type& str, int64_t& value) const = 0; + /// Parse the string and return the number of used characters. If it returns 0 + /// then parsing failed. + virtual size_t parse(const string_type& str, int32_t& value) const = 0; + + /// Get formatter for the current state of ios_base -- flags and locale, + /// NULL may be returned if an invalid combination of flags is provided or this type + /// of formatting is not supported by locale. + /// + /// Note: formatter is cached. If \a ios is not changed (no flags or locale changed) + /// the formatter would remain the same. Otherwise it would be rebuild and cached + /// for future use. It is useful for saving time for generation + /// of multiple values with same locale. + /// + /// For example this code will create a new spelling formatter only once: + /// + /// \code + /// std::cout << as::spellout; + /// for(int i=1;i<=10;i++) + /// std::cout << i << std::endl; + /// \endcode + /// + /// + static std::unique_ptr + create(std::ios_base& ios, const icu::Locale& locale, const std::string& encoding); + }; // class formatter + +}}} // namespace boost::locale::impl_icu + +#endif diff --git a/libs/locale/src/boost/locale/icu/formatters_cache.cpp b/libs/locale/src/boost/locale/icu/formatters_cache.cpp new file mode 100644 index 0000000..c9a8c46 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/formatters_cache.cpp @@ -0,0 +1,143 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2021-2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/icu/formatters_cache.hpp" +#include +#include +#include +#include +#ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable : 4251) // "identifier" : class "type" needs to have dll-interface... +#endif +#include +#include +#include +#include +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +namespace boost { namespace locale { namespace impl_icu { + + std::locale::id formatters_cache::id; + + namespace { + + struct init { + init() { ignore_unused(std::has_facet(std::locale::classic())); } + } instance; + + void get_icu_pattern(std::unique_ptr fmt, icu::UnicodeString& out_str) + { + icu::SimpleDateFormat* sfmt = icu_cast(fmt.get()); + if(sfmt) + sfmt->toPattern(out_str); + else + out_str.remove(); // LCOV_EXCL_LINE + } + void get_icu_pattern(icu::DateFormat* fmt, icu::UnicodeString& out_str) + { + return get_icu_pattern(std::unique_ptr(fmt), out_str); + } + } // namespace + + formatters_cache::formatters_cache(const icu::Locale& locale) : locale_(locale) + { +#define BOOST_LOCALE_ARRAY_SIZE(T) std::extent::type>::value + constexpr icu::DateFormat::EStyle styles[]{icu::DateFormat::kShort, + icu::DateFormat::kMedium, + icu::DateFormat::kLong, + icu::DateFormat::kFull}; + constexpr int num_styles = BOOST_LOCALE_ARRAY_SIZE(styles); + + static_assert(num_styles == BOOST_LOCALE_ARRAY_SIZE(date_format_), "!"); + for(int i = 0; i < num_styles; i++) + get_icu_pattern(icu::DateFormat::createDateInstance(styles[i], locale), date_format_[i]); + + static_assert(num_styles == BOOST_LOCALE_ARRAY_SIZE(time_format_), "!"); + for(int i = 0; i < num_styles; i++) + get_icu_pattern(icu::DateFormat::createTimeInstance(styles[i], locale), time_format_[i]); + + static_assert(num_styles == BOOST_LOCALE_ARRAY_SIZE(date_time_format_), "!"); + static_assert(num_styles == BOOST_LOCALE_ARRAY_SIZE(date_time_format_[0]), "!"); + for(int i = 0; i < num_styles; i++) { + for(int j = 0; j < num_styles; j++) { + get_icu_pattern(icu::DateFormat::createDateTimeInstance(styles[i], styles[j], locale), + date_time_format_[i][j]); + } + } +#undef BOOST_LOCALE_ARRAY_SIZE + + const auto get_str_or = [](const icu::UnicodeString& str, const char* default_str) { + return str.isEmpty() ? default_str : str; + }; + default_date_format_ = get_str_or(date_format(format_len::Medium), "yyyy-MM-dd"); + default_time_format_ = get_str_or(time_format(format_len::Medium), "HH:mm:ss"); + default_date_time_format_ = + get_str_or(date_time_format(format_len::Full, format_len::Full), "yyyy-MM-dd HH:mm:ss"); + } + + icu::NumberFormat* formatters_cache::create_number_format(num_fmt_type type, UErrorCode& err) const + { + switch(type) { + case num_fmt_type::number: return icu::NumberFormat::createInstance(locale_, err); break; + case num_fmt_type::sci: return icu::NumberFormat::createScientificInstance(locale_, err); break; +#if BOOST_LOCALE_ICU_VERSION >= 408 + case num_fmt_type::curr_nat: return icu::NumberFormat::createInstance(locale_, UNUM_CURRENCY, err); break; + case num_fmt_type::curr_iso: + return icu::NumberFormat::createInstance(locale_, UNUM_CURRENCY_ISO, err); + break; +#elif BOOST_LOCALE_ICU_VERSION >= 402 + case num_fmt_type::curr_nat: + return icu::NumberFormat::createInstance(locale_, icu::NumberFormat::kCurrencyStyle, err); + break; + case num_fmt_type::curr_iso: + return icu::NumberFormat::createInstance(locale_, icu::NumberFormat::kIsoCurrencyStyle, err); + break; +#else + case num_fmt_type::curr_nat: + case num_fmt_type::curr_iso: return icu::NumberFormat::createCurrencyInstance(locale_, err); break; +#endif + case num_fmt_type::percent: return icu::NumberFormat::createPercentInstance(locale_, err); break; + case num_fmt_type::spell: return new icu::RuleBasedNumberFormat(icu::URBNF_SPELLOUT, locale_, err); break; + case num_fmt_type::ordinal: return new icu::RuleBasedNumberFormat(icu::URBNF_ORDINAL, locale_, err); break; + } + throw std::logic_error("locale::internal error should not get there"); // LCOV_EXCL_LINE + } + + icu::NumberFormat& formatters_cache::number_format(num_fmt_type type) const + { + icu::NumberFormat* result = number_format_[int(type)].get(); + if(!result) { + UErrorCode err = U_ZERO_ERROR; + std::unique_ptr new_ptr(create_number_format(type, err)); + check_and_throw_icu_error(err, "Failed to create a formatter"); + result = new_ptr.get(); + BOOST_ASSERT(result); + number_format_[int(type)].reset(new_ptr.release()); + } + return *result; + } + + icu::SimpleDateFormat* formatters_cache::date_formatter() const + { + icu::SimpleDateFormat* result = date_formatter_.get(); + if(!result) { + std::unique_ptr fmt( + icu::DateFormat::createDateTimeInstance(icu::DateFormat::kMedium, icu::DateFormat::kMedium, locale_)); + + result = icu_cast(fmt.get()); + if(result) { + fmt.release(); + date_formatter_.reset(result); + } + } + return result; + } + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/formatters_cache.hpp b/libs/locale/src/boost/locale/icu/formatters_cache.hpp new file mode 100644 index 0000000..d0e4978 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/formatters_cache.hpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2021-2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_PREDEFINED_FORMATTERS_HPP_INCLUDED +#define BOOST_LOCALE_PREDEFINED_FORMATTERS_HPP_INCLUDED + +#include +#include "boost/locale/icu/icu_util.hpp" +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable : 4251) // "identifier" : class "type" needs to have dll-interface... +#endif +#include +#include +#include +#ifdef BOOST_MSVC +# pragma warning(pop) +#endif + +namespace boost { namespace locale { namespace impl_icu { + + enum class format_len { + Short, + Medium, + Long, + Full, + }; + + enum class num_fmt_type { number, sci, curr_nat, curr_iso, percent, spell, ordinal }; + + class formatters_cache : public std::locale::facet { + public: + static std::locale::id id; + + formatters_cache(const icu::Locale& locale); + + icu::NumberFormat& number_format(num_fmt_type type) const; + + const icu::UnicodeString& date_format(format_len f) const { return date_format_[int(f)]; } + + const icu::UnicodeString& time_format(format_len f) const { return time_format_[int(f)]; } + + const icu::UnicodeString& date_time_format(format_len d, format_len t) const + { + return date_time_format_[int(d)][int(t)]; + } + + const icu::UnicodeString& default_date_format() const { return default_date_format_; } + const icu::UnicodeString& default_time_format() const { return default_time_format_; } + const icu::UnicodeString& default_date_time_format() const { return default_date_time_format_; } + + icu::SimpleDateFormat* date_formatter() const; + + private: + icu::NumberFormat* create_number_format(num_fmt_type type, UErrorCode& err) const; + + static constexpr auto num_fmt_type_count = static_cast(num_fmt_type::ordinal) + 1; + static constexpr auto format_len_count = static_cast(format_len::Full) + 1; + + mutable boost::thread_specific_ptr number_format_[num_fmt_type_count]; + icu::UnicodeString date_format_[format_len_count]; + icu::UnicodeString time_format_[format_len_count]; + icu::UnicodeString date_time_format_[format_len_count][format_len_count]; + icu::UnicodeString default_date_format_, default_time_format_, default_date_time_format_; + mutable boost::thread_specific_ptr date_formatter_; + icu::Locale locale_; + }; + +}}} // namespace boost::locale::impl_icu + +#endif diff --git a/libs/locale/src/boost/locale/icu/icu_backend.cpp b/libs/locale/src/boost/locale/icu/icu_backend.cpp new file mode 100644 index 0000000..acf1afc --- /dev/null +++ b/libs/locale/src/boost/locale/icu/icu_backend.cpp @@ -0,0 +1,135 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/icu/icu_backend.hpp" +#include +#include +#include +#include +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/cdata.hpp" +#include +#include + +#include + +namespace boost { namespace locale { namespace impl_icu { + class icu_localization_backend : public localization_backend { + public: + icu_localization_backend() : invalid_(true), use_ansi_encoding_(false) {} + icu_localization_backend(const icu_localization_backend& other) : + localization_backend(), paths_(other.paths_), domains_(other.domains_), locale_id_(other.locale_id_), + invalid_(true), use_ansi_encoding_(other.use_ansi_encoding_) + {} + icu_localization_backend* clone() const override { return new icu_localization_backend(*this); } + + void set_option(const std::string& name, const std::string& value) override + { + invalid_ = true; + if(name == "locale") + locale_id_ = value; + else if(name == "message_path") + paths_.push_back(value); + else if(name == "message_application") + domains_.push_back(value); + else if(name == "use_ansi_encoding") + use_ansi_encoding_ = value == "true"; + } + void clear_options() override + { + invalid_ = true; + use_ansi_encoding_ = false; + locale_id_.clear(); + paths_.clear(); + domains_.clear(); + } + + void prepare_data() + { + if(!invalid_) + return; + invalid_ = false; + real_id_ = locale_id_; + if(real_id_.empty()) { + bool utf8 = !use_ansi_encoding_; + real_id_ = util::get_system_locale(utf8); + } + + util::locale_data d; + d.parse(real_id_); + + data_.locale = icu::Locale::createCanonical(real_id_.c_str()); + data_.encoding = d.encoding(); + data_.utf8 = d.is_utf8(); + language_ = d.language(); + country_ = d.country(); + variant_ = d.variant(); + } + + std::locale install(const std::locale& base, category_t category, char_facet_t type) override + { + prepare_data(); + + switch(category) { + case category_t::convert: return create_convert(base, data_, type); + case category_t::collation: return create_collate(base, data_, type); + case category_t::formatting: return create_formatting(base, data_, type); + case category_t::parsing: return create_parsing(base, data_, type); + case category_t::codepage: return create_codecvt(base, data_.encoding, type); + case category_t::message: { + gnu_gettext::messages_info minf; + minf.language = language_; + minf.country = country_; + minf.variant = variant_; + minf.encoding = data_.encoding; + std::copy(domains_.begin(), + domains_.end(), + std::back_inserter(minf.domains)); + minf.paths = paths_; + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); + case char_facet_t::wchar_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif + } + return base; + } + case category_t::boundary: return create_boundary(base, data_, type); + case category_t::calendar: return create_calendar(base, data_); + case category_t::information: return util::create_info(base, real_id_); + } + return base; + } + + private: + std::vector paths_; + std::vector domains_; + std::string locale_id_; + + cdata data_; + std::string language_; + std::string country_; + std::string variant_; + std::string real_id_; + bool invalid_; + bool use_ansi_encoding_; + }; + + localization_backend* create_localization_backend() + { + return new icu_localization_backend(); + } + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/icu_backend.hpp b/libs/locale/src/boost/locale/icu/icu_backend.hpp new file mode 100644 index 0000000..3dc2ca5 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/icu_backend.hpp @@ -0,0 +1,18 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_ICU_LOCALIZATION_BACKEND_HPP +#define BOOST_LOCALE_IMPL_ICU_LOCALIZATION_BACKEND_HPP + +#include + +namespace boost { namespace locale { + class localization_backend; + namespace impl_icu { + localization_backend* create_localization_backend(); + } // namespace impl_icu +}} // namespace boost::locale +#endif diff --git a/libs/locale/src/boost/locale/icu/icu_util.hpp b/libs/locale/src/boost/locale/icu/icu_util.hpp new file mode 100644 index 0000000..871e3f8 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/icu_util.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2021-2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_SRC_ICU_UTIL_HPP +#define BOOST_SRC_ICU_UTIL_HPP + +#include +#ifdef BOOST_HAS_STDINT_H +# include // Avoid ICU defining e.g. INT8_MIN causing macro redefinition warnings +#endif +#include +#include +#include +#include + +#define BOOST_LOCALE_ICU_VERSION (U_ICU_VERSION_MAJOR_NUM * 100 + U_ICU_VERSION_MINOR_NUM) + +namespace boost { namespace locale { namespace impl_icu { + + inline void throw_icu_error(UErrorCode err, std::string desc) // LCOV_EXCL_LINE + { + if(!desc.empty()) // LCOV_EXCL_LINE + desc += ": "; // LCOV_EXCL_LINE + throw std::runtime_error(desc + u_errorName(err)); // LCOV_EXCL_LINE + } + + inline void check_and_throw_icu_error(UErrorCode err, const char* desc = "") + { + if(U_FAILURE(err)) + throw_icu_error(err, desc); // LCOV_EXCL_LINE + } + + /// Cast a pointer to an ICU object to a pointer to TargetType + /// using RTTI or ICUs "poor man's RTTI" to make it work with e.g. libc++ and hidden visibility + template + TargetType* icu_cast(SourceType* p) + { + TargetType* result = dynamic_cast(p); + if(!result && p && p->getDynamicClassID() == TargetType::getStaticClassID()) + result = static_cast(p); + return result; + } +}}} // namespace boost::locale::impl_icu + +#endif diff --git a/libs/locale/src/boost/locale/icu/numeric.cpp b/libs/locale/src/boost/locale/icu/numeric.cpp new file mode 100644 index 0000000..d3fdaaa --- /dev/null +++ b/libs/locale/src/boost/locale/icu/numeric.cpp @@ -0,0 +1,367 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include "boost/locale/icu/all_generator.hpp" +#include "boost/locale/icu/cdata.hpp" +#include "boost/locale/icu/formatter.hpp" +#include "boost/locale/icu/formatters_cache.hpp" +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace impl_icu { + + namespace detail { + template::is_integer> + struct icu_format_type; + + template + struct icu_format_type { + // ICU supports 32 and 64 bit ints, use the former as long as it fits, else the latter + typedef typename std::conditional::digits <= 31, int32_t, int64_t>::type type; + }; + template + struct icu_format_type { + // Only float type ICU supports is double + typedef double type; + }; + + // ICU does not support uint64_t values so fall back to the parent/std formatting + // if the number is to large to fit into an int64_t + template::is_signed && std::numeric_limits::is_integer + && (sizeof(T) >= sizeof(uint64_t))> + struct use_parent_traits { + static bool use(T /*v*/) { return false; } + }; + template + struct use_parent_traits { + static bool use(T v) { return v > static_cast(std::numeric_limits::max()); } + }; + + template + static bool use_parent(std::ios_base& ios, ValueType v) + { + const uint64_t flg = ios_info::get(ios).display_flags(); + if(flg == flags::posix) + return true; + if(use_parent_traits::use(v)) + return true; + + if(!std::numeric_limits::is_integer) + return false; + + if(flg == flags::number && (ios.flags() & std::ios_base::basefield) != std::ios_base::dec) { + return true; + } + return false; + } + } // namespace detail + + template + class num_format : public std::num_put { + public: + typedef typename std::num_put::iter_type iter_type; + typedef std::basic_string string_type; + typedef CharType char_type; + typedef formatter formatter_type; + + num_format(const cdata& d, size_t refs = 0) : std::num_put(refs), loc_(d.locale), enc_(d.encoding) {} + + protected: + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, long val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, unsigned long val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, double val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, long double val) const override + { + return do_real_put(out, ios, fill, val); + } + + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, long long val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, unsigned long long val) const override + { + return do_real_put(out, ios, fill, val); + } + + private: + template + iter_type do_real_put(iter_type out, std::ios_base& ios, char_type fill, ValueType val) const + { + if(detail::use_parent(ios, val)) + return std::num_put::do_put(out, ios, fill, val); + + const auto formatter = formatter_type::create(ios, loc_, enc_); + + if(!formatter) + return std::num_put::do_put(out, ios, fill, val); + + size_t code_points; + typedef typename detail::icu_format_type::type icu_type; + const string_type& str = formatter->format(static_cast(val), code_points); + std::streamsize on_left = 0, on_right = 0, points = code_points; + if(points < ios.width()) { + std::streamsize n = ios.width() - points; + + std::ios_base::fmtflags flags = ios.flags() & std::ios_base::adjustfield; + + // We do not really know internal point, so we assume that it does not + // exist. So according to the standard field should be right aligned + if(flags != std::ios_base::left) + on_left = n; + on_right = n - on_left; + } + while(on_left > 0) { + *out++ = fill; + on_left--; + } + std::copy(str.begin(), str.end(), out); + while(on_right > 0) { + *out++ = fill; + on_right--; + } + ios.width(0); + return out; + } + + icu::Locale loc_; + std::string enc_; + + }; /// num_format + + template + class num_parse : public std::num_get { + public: + num_parse(const cdata& d, size_t refs = 0) : std::num_get(refs), loc_(d.locale), enc_(d.encoding) {} + + protected: + typedef typename std::num_get::iter_type iter_type; + typedef std::basic_string string_type; + typedef CharType char_type; + typedef formatter formatter_type; + typedef std::basic_istream stream_type; + + iter_type + do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned short& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned int& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type + do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, float& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type + do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, double& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + long double& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + long long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned long long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + private: + // + // This is not really an efficient solution, but it works + // + template + iter_type + do_real_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, ValueType& val) const + { + stream_type* stream_ptr = dynamic_cast(&ios); + if(!stream_ptr || detail::use_parent(ios, ValueType(0))) { + return std::num_get::do_get(in, end, ios, err, val); + } + + const auto formatter = formatter_type::create(ios, loc_, enc_); + if(!formatter) { + return std::num_get::do_get(in, end, ios, err, val); + } + + string_type tmp; + tmp.reserve(64); + + CharType c; + while(in != end && (((c = *in) <= 32 && (c > 0)) || c == 127)) // Assuming that ASCII is a subset + ++in; + + while(tmp.size() < 4096 && in != end && *in != '\n') { + tmp += *in++; + } + + typedef typename detail::icu_format_type::type icu_type; + icu_type value; + size_t parsed_chars; + + if((parsed_chars = formatter->parse(tmp, value)) == 0 || !is_losless_castable(value)) { + err |= std::ios_base::failbit; + } else { + val = static_cast(value); + } + + for(size_t n = tmp.size(); n > parsed_chars; n--) { + stream_ptr->putback(tmp[n - 1]); + } + + in = iter_type(*stream_ptr); + + if(in == end) + err |= std::ios_base::eofbit; + return in; + } + + BOOST_LOCALE_START_CONST_CONDITION + template + bool is_losless_castable(SrcType v) const + { + typedef std::numeric_limits target_limits; + typedef std::numeric_limits casted_limits; + if(v < 0 && !target_limits::is_signed) + return false; + + constexpr TargetType max_val = target_limits::max(); + + if(sizeof(SrcType) > sizeof(TargetType) && v > static_cast(max_val)) + return false; + + if(target_limits::is_integer == casted_limits::is_integer) + return true; + + if(target_limits::is_integer) { // and source is not + if(static_cast(static_cast(v)) != v) + return false; + } + return true; + } + BOOST_LOCALE_END_CONST_CONDITION + + icu::Locale loc_; + std::string enc_; + }; + + template + std::locale install_formatting_facets(const std::locale& in, const cdata& cd) + { + std::locale tmp = std::locale(in, new num_format(cd)); + if(!std::has_facet(in)) { + tmp = std::locale(tmp, new formatters_cache(cd.locale)); + } + return tmp; + } + + template + std::locale install_parsing_facets(const std::locale& in, const cdata& cd) + { + std::locale tmp = std::locale(in, new num_parse(cd)); + if(!std::has_facet(in)) { + tmp = std::locale(tmp, new formatters_cache(cd.locale)); + } + return tmp; + } + + std::locale create_formatting(const std::locale& in, const cdata& cd, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return install_formatting_facets(in, cd); + case char_facet_t::wchar_f: return install_formatting_facets(in, cd); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return install_formatting_facets(in, cd); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return install_formatting_facets(in, cd); +#endif + } + return in; + } + + std::locale create_parsing(const std::locale& in, const cdata& cd, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return install_parsing_facets(in, cd); + case char_facet_t::wchar_f: return install_parsing_facets(in, cd); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return install_parsing_facets(in, cd); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return install_parsing_facets(in, cd); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_icu + +// boostinspect:nominmax diff --git a/libs/locale/src/boost/locale/icu/time_zone.cpp b/libs/locale/src/boost/locale/icu/time_zone.cpp new file mode 100644 index 0000000..dc69f2d --- /dev/null +++ b/libs/locale/src/boost/locale/icu/time_zone.cpp @@ -0,0 +1,217 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/icu/time_zone.hpp" +#include +#include "boost/locale/icu/icu_util.hpp" +#include + +// +// Bug - when ICU tries to find a file that is equivalent to /etc/localtime it finds /usr/share/zoneinfo/localtime +// that is just a symbolic link to /etc/localtime. +// +// It started in 4.0 and was fixed in version 4.6, also the fix was backported to the 4.4 branch so it should be +// available from 4.4.3... So we test if the workaround is required +// +// It is also relevant only for Linux, BSD and Apple (as I see in ICU code) +// + +#if BOOST_LOCALE_ICU_VERSION >= 400 && BOOST_LOCALE_ICU_VERSION <= 406 \ + && (BOOST_LOCALE_ICU_VERSION != 404 || U_ICU_VERSION_PATCHLEVEL_NUM >= 3) +# if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__) +# define BOOST_LOCALE_WORKAROUND_ICU_BUG +# endif +#endif + +#ifdef BOOST_LOCALE_WORKAROUND_ICU_BUG +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +namespace boost { namespace locale { namespace impl_icu { + +#ifndef BOOST_LOCALE_WORKAROUND_ICU_BUG + + // This is normal behavior + + icu::TimeZone* get_time_zone(const std::string& time_zone) + { + if(time_zone.empty()) { + return icu::TimeZone::createDefault(); + } else { + icu::TimeZone* icu_tz = icu::TimeZone::createTimeZone(time_zone.c_str()); + return icu_tz; + } + } + +#else + + // This is a workaround for an ICU timezone detection bug. + // It is \b very ICU specific and should not be used + // in general. It is also designed to work only on + // specific patforms: Linux, BSD and Apple, where this bug may actually + // occur + namespace { + + // Under BSD, Linux and Mac OS X dirent has normal size + // so no issues with readdir_r + + class directory { + public: + directory(const char* name) : d(0), read_result(0) + { + d = opendir(name); + if(!d) + return; + } + ~directory() + { + if(d) + closedir(d); + } + bool is_open() { return d; } + const char* next() + { + if(d && readdir_r(d, &de, &read_result) == 0 && read_result != 0) + return de.d_name; + return 0; + } + + private: + DIR* d; + struct dirent de; + struct dirent* read_result; + }; + + bool files_equal(const std::string& left, const std::string& right) + { + char l[256], r[256]; + std::ifstream ls(left.c_str()); + if(!ls) + return false; + std::ifstream rs(right.c_str()); + if(!rs) + return false; + do { + ls.read(l, sizeof(l)); + rs.read(r, sizeof(r)); + size_t n; + if((n = ls.gcount()) != size_t(rs.gcount())) + return false; + if(memcmp(l, r, n) != 0) + return false; + } while(!ls.eof() || !rs.eof()); + if(bool(ls.eof()) != bool(rs.eof())) + return false; + return true; + } + + std::string find_file_in(const std::string& ref, size_t size, const std::string& dir) + { + directory d(dir.c_str()); + if(!d.is_open()) + return std::string(); + + const char* name = 0; + while((name = d.next()) != 0) { + std::string file_name = name; + if(file_name == "." || file_name == ".." || file_name == "posixrules" || file_name == "localtime") { + continue; + } + struct stat st; + std::string path = dir + "/" + file_name; + if(stat(path.c_str(), &st) == 0) { + if(S_ISDIR(st.st_mode)) { + std::string res = find_file_in(ref, size, path); + if(!res.empty()) + return file_name + "/" + res; + } else { + if(size_t(st.st_size) == size && files_equal(path, ref)) { + return file_name; + } + } + } + } + return std::string(); + } + + // This actually emulates ICU's search + // algorithm... just it ignores localtime + std::string detect_correct_time_zone() + { + const char* tz_dir = "/usr/share/zoneinfo"; + const char* tz_file = "/etc/localtime"; + + struct stat st; + if(::stat(tz_file, &st) != 0) + return std::string(); + size_t size = st.st_size; + std::string r = find_file_in(tz_file, size, tz_dir); + if(r.empty()) + return r; + if(r.compare(0, 6, "posix/") == 0 || r.compare(0, 6, "right/", 6) == 0) + return r.substr(6); + return r; + } + + // Using pthread as: + // - This bug is relevant for only Linux, BSD, Mac OS X and + // pthreads are native threading API + // - The dependency on boost.thread may be removed when using + // more recent ICU versions (so TLS would not be needed) + // + // This the dependency on Boost.Thread is eliminated + + pthread_once_t init_tz = PTHREAD_ONCE_INIT; + std::string default_time_zone_name; + + extern "C" { + static void init_tz_proc() + { + try { + default_time_zone_name = detect_correct_time_zone(); + } catch(...) { + } + } + } + + std::string get_time_zone_name() + { + pthread_once(&init_tz, init_tz_proc); + return default_time_zone_name; + } + + } // namespace + + icu::TimeZone* get_time_zone(const std::string& time_zone) + { + if(!time_zone.empty()) { + return icu::TimeZone::createTimeZone(time_zone.c_str()); + } + hold_ptr tz(icu::TimeZone::createDefault()); + icu::UnicodeString id; + tz->getID(id); + // Check if there is a bug? + if(id != icu::UnicodeString("localtime")) + return tz.release(); + // Now let's deal with the bug and run the fixed + // search loop as that of ICU + std::string real_id = get_time_zone_name(); + if(real_id.empty()) { + // if we failed fallback to ICU's time zone + return tz.release(); + } + return icu::TimeZone::createTimeZone(real_id.c_str()); + } +#endif // bug workaround + +}}} // namespace boost::locale::impl_icu diff --git a/libs/locale/src/boost/locale/icu/time_zone.hpp b/libs/locale/src/boost/locale/icu/time_zone.hpp new file mode 100644 index 0000000..132c5db --- /dev/null +++ b/libs/locale/src/boost/locale/icu/time_zone.hpp @@ -0,0 +1,24 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_ICU_GET_TIME_ZONE_HPP +#define BOOST_LOCALE_IMPL_ICU_GET_TIME_ZONE_HPP + +#include +#ifdef BOOST_HAS_STDINT_H +# include // Avoid ICU defining e.g. INT8_MIN causing macro redefinition warnings +#endif +#include +#include + +namespace boost { namespace locale { namespace impl_icu { + + // Provides a workaround for an ICU default timezone bug and also + // handles time_zone string correctly - if empty returns default + // otherwise returns the instance created with time_zone + icu::TimeZone* get_time_zone(const std::string& time_zone); +}}} // namespace boost::locale::impl_icu +#endif diff --git a/libs/locale/src/boost/locale/icu/uconv.hpp b/libs/locale/src/boost/locale/icu/uconv.hpp new file mode 100644 index 0000000..ac82782 --- /dev/null +++ b/libs/locale/src/boost/locale/icu/uconv.hpp @@ -0,0 +1,306 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_SRC_LOCALE_ICU_UCONV_HPP +#define BOOST_SRC_LOCALE_ICU_UCONV_HPP + +#include +#include "boost/locale/icu/icu_util.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable : 4244) // 'argument' : conversion from 'int' +# pragma warning(disable : 4267) // 'argument' : conversion from 'size_t' +#endif + +namespace boost { namespace locale { namespace impl_icu { + + enum class cpcvt_type { skip, stop }; + + template + class icu_std_converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + icu_std_converter(std::string charset, cpcvt_type cv = cpcvt_type::skip); + icu::UnicodeString icu(const char_type* begin, const char_type* end) const; + string_type std(const icu::UnicodeString& str) const; + size_t cut(const icu::UnicodeString& str, + const char_type* begin, + const char_type* end, + size_t n, + size_t from_u = 0, + size_t from_c = 0) const; + }; + + template + class icu_std_converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + icu::UnicodeString icu_checked(const char_type* vb, const char_type* ve) const + { + return icu(vb, ve); // Already done + } + icu::UnicodeString icu(const char_type* vb, const char_type* ve) const + { + const char* begin = reinterpret_cast(vb); + const char* end = reinterpret_cast(ve); + uconv cvt(charset_, cvt_type_); + UErrorCode err = U_ZERO_ERROR; + icu::UnicodeString tmp(begin, end - begin, cvt.cvt(), err); + check_and_throw_icu_error(err); + return tmp; + } + + string_type std(const icu::UnicodeString& str) const + { + uconv cvt(charset_, cvt_type_); + return cvt.go(str.getBuffer(), str.length(), max_len_); + } + + icu_std_converter(std::string charset, cpcvt_type cvt_type = cpcvt_type::skip) : + charset_(charset), cvt_type_(cvt_type) + { + uconv cvt(charset_, cvt_type); + max_len_ = cvt.max_char_size(); + } + + size_t cut(const icu::UnicodeString& str, + const char_type* begin, + const char_type* end, + size_t n, + size_t from_u = 0, + size_t from_char = 0) const + { + size_t code_points = str.countChar32(from_u, n); + uconv cvt(charset_, cvt_type_); + return cvt.cut(code_points, begin + from_char, end); + } + + struct uconv { + uconv(const uconv& other) = delete; + void operator=(const uconv& other) = delete; + + uconv(const std::string& charset, cpcvt_type cvt_type = cpcvt_type::skip) + { + UErrorCode err = U_ZERO_ERROR; + cvt_ = ucnv_open(charset.c_str(), &err); + if(!cvt_ || U_FAILURE(err)) { + if(cvt_) + ucnv_close(cvt_); + throw conv::invalid_charset_error(charset); + } + + try { + if(cvt_type == cpcvt_type::skip) { + ucnv_setFromUCallBack(cvt_, UCNV_FROM_U_CALLBACK_SKIP, 0, 0, 0, &err); + check_and_throw_icu_error(err); + + err = U_ZERO_ERROR; + ucnv_setToUCallBack(cvt_, UCNV_TO_U_CALLBACK_SKIP, 0, 0, 0, &err); + check_and_throw_icu_error(err); + } else { + ucnv_setFromUCallBack(cvt_, UCNV_FROM_U_CALLBACK_STOP, 0, 0, 0, &err); + check_and_throw_icu_error(err); + + err = U_ZERO_ERROR; + ucnv_setToUCallBack(cvt_, UCNV_TO_U_CALLBACK_STOP, 0, 0, 0, &err); + check_and_throw_icu_error(err); + } + } catch(...) { + ucnv_close(cvt_); + throw; + } + } + + int max_char_size() { return ucnv_getMaxCharSize(cvt_); } + + string_type go(const UChar* buf, int length, int max_size) + { + string_type res; + res.resize(UCNV_GET_MAX_BYTES_FOR_STRING(length, max_size)); + char* ptr = reinterpret_cast(&res[0]); + UErrorCode err = U_ZERO_ERROR; + int n = ucnv_fromUChars(cvt_, ptr, res.size(), buf, length, &err); + check_and_throw_icu_error(err); + res.resize(n); + return res; + } + + size_t cut(size_t n, const char_type* begin, const char_type* end) + { + const char_type* saved = begin; + while(n > 0 && begin < end) { + UErrorCode err = U_ZERO_ERROR; + ucnv_getNextUChar(cvt_, &begin, end, &err); + if(U_FAILURE(err)) + return 0; + n--; + } + return begin - saved; + } + + UConverter* cvt() { return cvt_; } + + ~uconv() { ucnv_close(cvt_); } + + private: + UConverter* cvt_; + }; + + private: + int max_len_; + std::string charset_; + cpcvt_type cvt_type_; + }; + + template + class icu_std_converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + icu::UnicodeString icu_checked(const char_type* begin, const char_type* end) const + { + icu::UnicodeString tmp(end - begin, 0, 0); // make inital capacity + while(begin != end) { + UChar cl = *begin++; + if(U16_IS_SINGLE(cl)) + tmp.append(static_cast(cl)); + else if(U16_IS_LEAD(cl)) { + if(begin == end) { + throw_if_needed(); + } else { + UChar ct = *begin++; + if(!U16_IS_TRAIL(ct)) + throw_if_needed(); + else { + UChar32 c = U16_GET_SUPPLEMENTARY(cl, ct); + tmp.append(c); + } + } + } else + throw_if_needed(); + } + return tmp; + } + void throw_if_needed() const + { + if(mode_ == cpcvt_type::stop) + throw conv::conversion_error(); + } + icu::UnicodeString icu(const char_type* vb, const char_type* ve) const + { + const UChar* begin = reinterpret_cast(vb); + const UChar* end = reinterpret_cast(ve); + icu::UnicodeString tmp(begin, end - begin); + return tmp; + } + + string_type std(const icu::UnicodeString& str) const + { + const char_type* ptr = reinterpret_cast(str.getBuffer()); + return string_type(ptr, str.length()); + } + size_t cut(const icu::UnicodeString& /*str*/, + const char_type* /*begin*/, + const char_type* /*end*/, + size_t n, + size_t /*from_u*/ = 0, + size_t /*from_c*/ = 0) const + { + return n; + } + + icu_std_converter(std::string /*charset*/, cpcvt_type mode = cpcvt_type::skip) : mode_(mode) {} + + private: + cpcvt_type mode_; + }; + + template + class icu_std_converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + + icu::UnicodeString icu_checked(const char_type* begin, const char_type* end) const + { + icu::UnicodeString tmp(end - begin, 0, 0); // make inital capacity + while(begin != end) { + UChar32 c = static_cast(*begin++); + if(U_IS_UNICODE_CHAR(c)) + tmp.append(c); + else + throw_if_needed(); + } + return tmp; + } + void throw_if_needed() const + { + if(mode_ == cpcvt_type::stop) + throw conv::conversion_error(); + } + + icu::UnicodeString icu(const char_type* begin, const char_type* end) const + { + icu::UnicodeString tmp(end - begin, 0, 0); // make inital capacity + while(begin != end) { + UChar32 c = static_cast(*begin++); + tmp.append(c); + } + return tmp; + } + + string_type std(const icu::UnicodeString& str) const + { + string_type tmp; + tmp.resize(str.length()); + UChar32* ptr = reinterpret_cast(&tmp[0]); + int32_t len = 0; + UErrorCode code = U_ZERO_ERROR; + u_strToUTF32(ptr, tmp.size(), &len, str.getBuffer(), str.length(), &code); + + check_and_throw_icu_error(code); + + tmp.resize(len); + + return tmp; + } + + size_t cut(const icu::UnicodeString& str, + const char_type* /*begin*/, + const char_type* /*end*/, + size_t n, + size_t from_u = 0, + size_t /*from_c*/ = 0) const + { + return str.countChar32(from_u, n); + } + + icu_std_converter(std::string /*charset*/, cpcvt_type mode = cpcvt_type::skip) : mode_(mode) {} + + private: + cpcvt_type mode_; + }; +}}} // namespace boost::locale::impl_icu + +#endif + +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif diff --git a/libs/locale/src/boost/locale/posix/all_generator.hpp b/libs/locale/src/boost/locale/posix/all_generator.hpp new file mode 100644 index 0000000..da4d4a8 --- /dev/null +++ b/libs/locale/src/boost/locale/posix/all_generator.hpp @@ -0,0 +1,32 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_POSIX_ALL_GENERATOR_HPP +#define BOOST_LOCALE_IMPL_POSIX_ALL_GENERATOR_HPP + +#include +#include +#include +#include + +#ifdef __APPLE__ +# include +#endif + +namespace boost { namespace locale { namespace impl_posix { + + std::locale create_convert(const std::locale& in, std::shared_ptr lc, char_facet_t type); + + std::locale create_collate(const std::locale& in, std::shared_ptr lc, char_facet_t type); + + std::locale create_formatting(const std::locale& in, std::shared_ptr lc, char_facet_t type); + + std::locale create_parsing(const std::locale& in, std::shared_ptr lc, char_facet_t type); + std::locale create_codecvt(const std::locale& in, const std::string& encoding, char_facet_t type); + +}}} // namespace boost::locale::impl_posix + +#endif diff --git a/libs/locale/src/boost/locale/posix/codecvt.cpp b/libs/locale/src/boost/locale/posix/codecvt.cpp new file mode 100644 index 0000000..d2c5acf --- /dev/null +++ b/libs/locale/src/boost/locale/posix/codecvt.cpp @@ -0,0 +1,28 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2022-2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include "boost/locale/posix/all_generator.hpp" +#include "boost/locale/shared/iconv_codecvt.hpp" +#include "boost/locale/util/encoding.hpp" +#include + +namespace boost { namespace locale { namespace impl_posix { + + std::locale create_codecvt(const std::locale& in, const std::string& encoding, char_facet_t type) + { + if(util::normalize_encoding(encoding) == "utf8") + return util::create_utf8_codecvt(in, type); + + try { + return util::create_simple_codecvt(in, encoding, type); + } catch(const conv::invalid_charset_error&) { + return util::create_codecvt(in, create_iconv_converter(encoding), type); + } + } + +}}} // namespace boost::locale::impl_posix diff --git a/libs/locale/src/boost/locale/posix/collate.cpp b/libs/locale/src/boost/locale/posix/collate.cpp new file mode 100644 index 0000000..410d8be --- /dev/null +++ b/libs/locale/src/boost/locale/posix/collate.cpp @@ -0,0 +1,98 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#if defined(__FreeBSD__) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "boost/locale/posix/all_generator.hpp" +#include "boost/locale/shared/mo_hash.hpp" + +namespace boost { namespace locale { namespace impl_posix { + + template + struct coll_traits; + + template<> + struct coll_traits { + static size_t xfrm(char* out, const char* in, size_t n, locale_t l) { return strxfrm_l(out, in, n, l); } + static size_t coll(const char* left, const char* right, locale_t l) { return strcoll_l(left, right, l); } + }; + + template<> + struct coll_traits { + static size_t xfrm(wchar_t* out, const wchar_t* in, size_t n, locale_t l) { return wcsxfrm_l(out, in, n, l); } + static size_t coll(const wchar_t* left, const wchar_t* right, locale_t l) { return wcscoll_l(left, right, l); } + }; + + template + class collator : public std::collate { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + collator(std::shared_ptr l, size_t refs = 0) : std::collate(refs), lc_(std::move(l)) {} + + int + do_compare(const char_type* lb, const char_type* le, const char_type* rb, const char_type* re) const override + { + string_type left(lb, le - lb); + string_type right(rb, re - rb); + int res = coll_traits::coll(left.c_str(), right.c_str(), *lc_); + if(res < 0) + return -1; + if(res > 0) + return 1; + return 0; + } + long do_hash(const char_type* b, const char_type* e) const override + { + string_type s(do_transform(b, e)); + const char* begin = reinterpret_cast(s.c_str()); + const char* end = begin + s.size() * sizeof(char_type); + return gnu_gettext::pj_winberger_hash_function(begin, end); + } + string_type do_transform(const char_type* b, const char_type* e) const override + { + string_type s(b, e - b); + std::vector buf((e - b) * 2 + 1); + size_t n = coll_traits::xfrm(&buf.front(), s.c_str(), buf.size(), *lc_); + if(n > buf.size()) { + buf.resize(n); + coll_traits::xfrm(&buf.front(), s.c_str(), n, *lc_); + } + return string_type(&buf.front(), n); + } + + private: + std::shared_ptr lc_; + }; + + std::locale create_collate(const std::locale& in, std::shared_ptr lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new collator(std::move(lc))); + case char_facet_t::wchar_f: return std::locale(in, new collator(std::move(lc))); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new collator(std::move(lc))); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new collator(std::move(lc))); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_posix diff --git a/libs/locale/src/boost/locale/posix/converter.cpp b/libs/locale/src/boost/locale/posix/converter.cpp new file mode 100644 index 0000000..4dfa6ba --- /dev/null +++ b/libs/locale/src/boost/locale/posix/converter.cpp @@ -0,0 +1,141 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include +#endif + +#include "boost/locale/posix/all_generator.hpp" + +namespace boost { namespace locale { namespace impl_posix { + + template + struct case_traits; + + template<> + struct case_traits { + static char lower(char c, locale_t lc) { return tolower_l(c, lc); } + static char upper(char c, locale_t lc) { return toupper_l(c, lc); } + }; + + template<> + struct case_traits { + static wchar_t lower(wchar_t c, locale_t lc) { return towlower_l(c, lc); } + static wchar_t upper(wchar_t c, locale_t lc) { return towupper_l(c, lc); } + }; + + template + class std_converter : public converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + typedef std::ctype ctype_type; + std_converter(std::shared_ptr lc, size_t refs = 0) : converter(refs), lc_(std::move(lc)) {} + string_type convert(converter_base::conversion_type how, + const char_type* begin, + const char_type* end, + int /*flags*/ = 0) const override + { + switch(how) { + case converter_base::upper_case: { + string_type res; + res.reserve(end - begin); + while(begin != end) { + res += case_traits::upper(*begin++, *lc_); + } + return res; + } + case converter_base::lower_case: + case converter_base::case_folding: { + string_type res; + res.reserve(end - begin); + while(begin != end) { + res += case_traits::lower(*begin++, *lc_); + } + return res; + } + case converter_base::normalization: + case converter_base::title_case: break; + } + return string_type(begin, end - begin); + } + + private: + std::shared_ptr lc_; + }; + + class utf8_converter : public converter { + public: + utf8_converter(std::shared_ptr lc, size_t refs = 0) : converter(refs), lc_(std::move(lc)) {} + std::string convert(converter_base::conversion_type how, + const char* begin, + const char* end, + int /*flags*/ = 0) const override + { + switch(how) { + case upper_case: { + std::wstring tmp = conv::to_utf(begin, end, "UTF-8"); + std::wstring wres; + wres.reserve(tmp.size()); + for(unsigned i = 0; i < tmp.size(); i++) + wres += towupper_l(tmp[i], *lc_); + return conv::from_utf(wres, "UTF-8"); + } + + case lower_case: + case case_folding: { + std::wstring tmp = conv::to_utf(begin, end, "UTF-8"); + std::wstring wres; + wres.reserve(tmp.size()); + for(unsigned i = 0; i < tmp.size(); i++) + wres += towlower_l(tmp[i], *lc_); + return conv::from_utf(wres, "UTF-8"); + } + case normalization: + case title_case: break; + } + return std::string(begin, end - begin); + } + + private: + std::shared_ptr lc_; + }; + + std::locale create_convert(const std::locale& in, std::shared_ptr lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: { + std::string encoding = nl_langinfo_l(CODESET, *lc); + for(unsigned i = 0; i < encoding.size(); i++) + if('A' <= encoding[i] && encoding[i] <= 'Z') + encoding[i] = encoding[i] - 'A' + 'a'; + if(encoding == "utf-8" || encoding == "utf8" || encoding == "utf_8") { + return std::locale(in, new utf8_converter(std::move(lc))); + } + return std::locale(in, new std_converter(std::move(lc))); + } + case char_facet_t::wchar_f: return std::locale(in, new std_converter(std::move(lc))); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new std_converter(std::move(lc))); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new std_converter(std::move(lc))); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_posix diff --git a/libs/locale/src/boost/locale/posix/numeric.cpp b/libs/locale/src/boost/locale/posix/numeric.cpp new file mode 100644 index 0000000..6c63a88 --- /dev/null +++ b/libs/locale/src/boost/locale/posix/numeric.cpp @@ -0,0 +1,441 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#if defined(__FreeBSD__) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "boost/locale/posix/all_generator.hpp" +#include "boost/locale/util/numeric.hpp" + +namespace boost { namespace locale { namespace impl_posix { + + template + class num_format : public util::base_num_format { + public: + typedef typename std::num_put::iter_type iter_type; + typedef std::basic_string string_type; + typedef CharType char_type; + + num_format(std::shared_ptr lc, size_t refs = 0) : + util::base_num_format(refs), lc_(std::move(lc)) + {} + + protected: + iter_type do_format_currency(bool intl, + iter_type out, + std::ios_base& /*ios*/, + char_type /*fill*/, + long double val) const override + { + char buf[4] = {}; + const char* format = intl ? "%i" : "%n"; + errno = 0; + ssize_t n = strfmon_l(buf, sizeof(buf), *lc_, format, static_cast(val)); + if(n >= 0) + return write_it(out, buf, n); + + for(std::vector tmp(sizeof(buf) * 2); tmp.size() <= 4098; tmp.resize(tmp.size() * 2)) { + n = strfmon_l(&tmp.front(), tmp.size(), *lc_, format, static_cast(val)); + if(n >= 0) + return write_it(out, &tmp.front(), n); + } + return out; + } + + std::ostreambuf_iterator write_it(std::ostreambuf_iterator out, const char* ptr, size_t n) const + { + for(size_t i = 0; i < n; i++) + *out++ = *ptr++; + return out; + } + + std::ostreambuf_iterator + write_it(std::ostreambuf_iterator out, const char* ptr, size_t n) const + { + std::wstring tmp = conv::to_utf(ptr, ptr + n, nl_langinfo_l(CODESET, *lc_)); + for(size_t i = 0; i < tmp.size(); i++) + *out++ = tmp[i]; + return out; + } + + private: + std::shared_ptr lc_; + + }; /// num_format + + namespace { + std::string do_ftime(const char* format, const struct tm* t, locale_t lc) + { + char buf[16]; + size_t n = strftime_l(buf, sizeof(buf), format, t, lc); + if(n == 0) { + // Note standard specifies that in case of error the function returns 0, + // however 0 may be actually valid output value of for example empty format + // or an output of %p in some locales + // + // Thus we try to guess that 1024 would be enough. + std::vector v(1024); + n = strftime_l(v.data(), 1024, format, t, lc); + return std::string(v.data(), n); + } + return std::string(buf, n); + } + template + std::basic_string do_ftime(const CharType* format, const struct tm* t, locale_t lc) + { + const std::string encoding = nl_langinfo_l(CODESET, lc); + const std::string nformat = conv::from_utf(format, encoding); + const std::string nres = do_ftime(nformat.c_str(), t, lc); + return conv::to_utf(nres, encoding); + } + } // namespace + + template + class time_put_posix : public std::time_put { + public: + time_put_posix(std::shared_ptr lc, size_t refs = 0) : + std::time_put(refs), lc_(std::move(lc)) + {} + typedef typename std::time_put::iter_type iter_type; + typedef CharType char_type; + typedef std::basic_string string_type; + + iter_type do_put(iter_type out, + std::ios_base& /*ios*/, + CharType /*fill*/, + const std::tm* tm, + char format, + char modifier) const override + { + char_type fmt[4] = {'%', + static_cast(modifier != 0 ? modifier : format), + static_cast(modifier == 0 ? '\0' : format)}; + string_type res = do_ftime(fmt, tm, *lc_); + for(unsigned i = 0; i < res.size(); i++) + *out++ = res[i]; + return out; + } + + private: + std::shared_ptr lc_; + }; + + template + class ctype_posix; + + template<> + class ctype_posix : public std::ctype { + public: + ctype_posix(std::shared_ptr lc) : lc_(std::move(lc)) {} + + bool do_is(mask m, char c) const + { + if((m & space) && isspace_l(c, *lc_)) + return true; + if((m & print) && isprint_l(c, *lc_)) + return true; + if((m & cntrl) && iscntrl_l(c, *lc_)) + return true; + if((m & upper) && isupper_l(c, *lc_)) + return true; + if((m & lower) && islower_l(c, *lc_)) + return true; + if((m & alpha) && isalpha_l(c, *lc_)) + return true; + if((m & digit) && isdigit_l(c, *lc_)) + return true; + if((m & xdigit) && isxdigit_l(c, *lc_)) + return true; + if((m & punct) && ispunct_l(c, *lc_)) + return true; + return false; + } + const char* do_is(const char* begin, const char* end, mask* m) const + { + while(begin != end) { + char c = *begin++; + int r = 0; + if(isspace_l(c, *lc_)) + r |= space; + if(isprint_l(c, *lc_)) + r |= cntrl; + if(iscntrl_l(c, *lc_)) + r |= space; + if(isupper_l(c, *lc_)) + r |= upper; + if(islower_l(c, *lc_)) + r |= lower; + if(isalpha_l(c, *lc_)) + r |= alpha; + if(isdigit_l(c, *lc_)) + r |= digit; + if(isxdigit_l(c, *lc_)) + r |= xdigit; + if(ispunct_l(c, *lc_)) + r |= punct; + // r actually should be mask, but some standard + // libraries (like STLPort) + // do not define operator | properly so using int+cast + *m++ = static_cast(r); + } + return begin; + } + const char* do_scan_is(mask m, const char* begin, const char* end) const + { + while(begin != end) + if(do_is(m, *begin)) + return begin; + return begin; + } + const char* do_scan_not(mask m, const char* begin, const char* end) const + { + while(begin != end) + if(!do_is(m, *begin)) + return begin; + return begin; + } + char toupper(char c) const { return toupper_l(c, *lc_); } + const char* toupper(char* begin, const char* end) const + { + for(; begin != end; begin++) + *begin = toupper_l(*begin, *lc_); + return begin; + } + char tolower(char c) const { return tolower_l(c, *lc_); } + const char* tolower(char* begin, const char* end) const + { + for(; begin != end; begin++) + *begin = tolower_l(*begin, *lc_); + return begin; + } + + private: + std::shared_ptr lc_; + }; + + template<> + class ctype_posix : public std::ctype { + public: + ctype_posix(std::shared_ptr lc) : lc_(std::move(lc)) {} + + bool do_is(mask m, wchar_t c) const + { + if((m & space) && iswspace_l(c, *lc_)) + return true; + if((m & print) && iswprint_l(c, *lc_)) + return true; + if((m & cntrl) && iswcntrl_l(c, *lc_)) + return true; + if((m & upper) && iswupper_l(c, *lc_)) + return true; + if((m & lower) && iswlower_l(c, *lc_)) + return true; + if((m & alpha) && iswalpha_l(c, *lc_)) + return true; + if((m & digit) && iswdigit_l(c, *lc_)) + return true; + if((m & xdigit) && iswxdigit_l(c, *lc_)) + return true; + if((m & punct) && iswpunct_l(c, *lc_)) + return true; + return false; + } + const wchar_t* do_is(const wchar_t* begin, const wchar_t* end, mask* m) const + { + while(begin != end) { + wchar_t c = *begin++; + int r = 0; + if(iswspace_l(c, *lc_)) + r |= space; + if(iswprint_l(c, *lc_)) + r |= cntrl; + if(iswcntrl_l(c, *lc_)) + r |= space; + if(iswupper_l(c, *lc_)) + r |= upper; + if(iswlower_l(c, *lc_)) + r |= lower; + if(iswalpha_l(c, *lc_)) + r |= alpha; + if(iswdigit_l(c, *lc_)) + r |= digit; + if(iswxdigit_l(c, *lc_)) + r |= xdigit; + if(iswpunct_l(c, *lc_)) + r |= punct; + // r actually should be mask, but some standard + // libraries (like STLPort) + // do not define operator | properly so using int+cast + *m++ = static_cast(r); + } + return begin; + } + const wchar_t* do_scan_is(mask m, const wchar_t* begin, const wchar_t* end) const + { + while(begin != end) + if(do_is(m, *begin)) + return begin; + return begin; + } + const wchar_t* do_scan_not(mask m, const wchar_t* begin, const wchar_t* end) const + { + while(begin != end) + if(!do_is(m, *begin)) + return begin; + return begin; + } + wchar_t toupper(wchar_t c) const { return towupper_l(c, *lc_); } + const wchar_t* toupper(wchar_t* begin, const wchar_t* end) const + { + for(; begin != end; begin++) + *begin = towupper_l(*begin, *lc_); + return begin; + } + wchar_t tolower(wchar_t c) const { return tolower_l(c, *lc_); } + const wchar_t* tolower(wchar_t* begin, const wchar_t* end) const + { + for(; begin != end; begin++) + *begin = tolower_l(*begin, *lc_); + return begin; + } + + private: + std::shared_ptr lc_; + }; + + struct basic_numpunct { + std::string grouping; + std::string thousands_sep; + std::string decimal_point; + basic_numpunct() : decimal_point(".") {} + basic_numpunct(locale_t lc) + { +#if defined(__APPLE__) || defined(__FreeBSD__) + lconv* cv = localeconv_l(lc); + grouping = cv->grouping; + thousands_sep = cv->thousands_sep; + decimal_point = cv->decimal_point; +#else + thousands_sep = nl_langinfo_l(THOUSEP, lc); + decimal_point = nl_langinfo_l(RADIXCHAR, lc); +# ifdef GROUPING + grouping = nl_langinfo_l(GROUPING, lc); +# endif +#endif + } + }; + + template + class num_punct_posix : public std::numpunct { + public: + typedef std::basic_string string_type; + num_punct_posix(locale_t lc, size_t refs = 0) : std::numpunct(refs) + { + basic_numpunct np(lc); + to_str(np.thousands_sep, thousands_sep_, lc); + to_str(np.decimal_point, decimal_point_, lc); + grouping_ = np.grouping; + if(thousands_sep_.size() > 1) + grouping_ = std::string(); + if(decimal_point_.size() > 1) + decimal_point_ = CharType('.'); + } + void to_str(std::string& s1, std::string& s2, locale_t /*lc*/) { s2.swap(s1); } + void to_str(std::string& s1, std::wstring& s2, locale_t lc) + { + s2 = conv::to_utf(s1, nl_langinfo_l(CODESET, lc)); + } + CharType do_decimal_point() const override { return *decimal_point_.c_str(); } + CharType do_thousands_sep() const override { return *thousands_sep_.c_str(); } + std::string do_grouping() const override { return grouping_; } + string_type do_truename() const override + { + static const char t[] = "true"; + return string_type(t, t + sizeof(t) - 1); + } + string_type do_falsename() const override + { + static const char t[] = "false"; + return string_type(t, t + sizeof(t) - 1); + } + + private: + string_type decimal_point_; + string_type thousands_sep_; + std::string grouping_; + }; + + template + std::locale create_formatting_impl(const std::locale& in, std::shared_ptr lc) + { + std::locale tmp = std::locale(in, new num_punct_posix(*lc)); + tmp = std::locale(tmp, new ctype_posix(lc)); + tmp = std::locale(tmp, new time_put_posix(lc)); + tmp = std::locale(tmp, new num_format(std::move(lc))); + return tmp; + } + + template + std::locale create_parsing_impl(const std::locale& in, std::shared_ptr lc) + { + std::locale tmp = std::locale(in, new num_punct_posix(*lc)); + tmp = std::locale(tmp, new ctype_posix(std::move(lc))); + tmp = std::locale(tmp, new util::base_num_parse()); + return tmp; + } + + std::locale create_formatting(const std::locale& in, std::shared_ptr lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return create_formatting_impl(in, std::move(lc)); + case char_facet_t::wchar_f: return create_formatting_impl(in, std::move(lc)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return create_formatting_impl(in, lc); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return create_formatting_impl(in, lc); +#endif + } + return in; + } + + std::locale create_parsing(const std::locale& in, std::shared_ptr lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return create_parsing_impl(in, std::move(lc)); + case char_facet_t::wchar_f: return create_parsing_impl(in, std::move(lc)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return create_parsing_impl(in, lc); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return create_parsing_impl(in, lc); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_posix diff --git a/libs/locale/src/boost/locale/posix/posix_backend.cpp b/libs/locale/src/boost/locale/posix/posix_backend.cpp new file mode 100644 index 0000000..df87fa1 --- /dev/null +++ b/libs/locale/src/boost/locale/posix/posix_backend.cpp @@ -0,0 +1,156 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/posix/posix_backend.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +# include +#endif + +#include "boost/locale/posix/all_generator.hpp" +#include "boost/locale/util/gregorian.hpp" + +namespace boost { namespace locale { namespace impl_posix { + + class posix_localization_backend : public localization_backend { + public: + posix_localization_backend() : invalid_(true) {} + posix_localization_backend(const posix_localization_backend& other) : + localization_backend(), paths_(other.paths_), domains_(other.domains_), locale_id_(other.locale_id_), + invalid_(true) + {} + posix_localization_backend* clone() const override { return new posix_localization_backend(*this); } + + void set_option(const std::string& name, const std::string& value) override + { + invalid_ = true; + if(name == "locale") + locale_id_ = value; + else if(name == "message_path") + paths_.push_back(value); + else if(name == "message_application") + domains_.push_back(value); + } + void clear_options() override + { + invalid_ = true; + locale_id_.clear(); + paths_.clear(); + domains_.clear(); + } + + static void free_locale_by_ptr(locale_t* lc) + { + freelocale(*lc); + delete lc; + } + + void prepare_data() + { + if(!invalid_) + return; + invalid_ = false; + lc_.reset(); + real_id_ = locale_id_; + if(real_id_.empty()) + real_id_ = util::get_system_locale(); + + locale_t tmp = newlocale(LC_ALL_MASK, real_id_.c_str(), 0); + + if(!tmp) { + tmp = newlocale(LC_ALL_MASK, "C", 0); + } + if(!tmp) { + throw std::runtime_error("newlocale failed"); + } + + locale_t* tmp_p = 0; + + try { + tmp_p = new locale_t(); + } catch(...) { + freelocale(tmp); + throw; + } + + *tmp_p = tmp; + lc_ = std::shared_ptr(tmp_p, free_locale_by_ptr); + } + + std::locale install(const std::locale& base, category_t category, char_facet_t type) override + { + prepare_data(); + + switch(category) { + case category_t::convert: return create_convert(base, lc_, type); + case category_t::collation: return create_collate(base, lc_, type); + case category_t::formatting: return create_formatting(base, lc_, type); + case category_t::parsing: return create_parsing(base, lc_, type); + case category_t::codepage: return create_codecvt(base, nl_langinfo_l(CODESET, *lc_), type); + case category_t::calendar: { + util::locale_data inf; + inf.parse(real_id_); + return util::install_gregorian_calendar(base, inf.country()); + } + case category_t::message: { + gnu_gettext::messages_info minf; + util::locale_data inf; + inf.parse(real_id_); + minf.language = inf.language(); + minf.country = inf.country(); + minf.variant = inf.variant(); + minf.encoding = inf.encoding(); + std::copy(domains_.begin(), + domains_.end(), + std::back_inserter(minf.domains)); + minf.paths = paths_; + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); + case char_facet_t::wchar_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif + } + return base; + } + case category_t::information: return util::create_info(base, real_id_); + case category_t::boundary: break; // Not implemented + } + return base; + } + + private: + std::vector paths_; + std::vector domains_; + std::string locale_id_; + std::string real_id_; + + bool invalid_; + std::shared_ptr lc_; + }; + + localization_backend* create_localization_backend() + { + return new posix_localization_backend(); + } + +}}} // namespace boost::locale::impl_posix diff --git a/libs/locale/src/boost/locale/posix/posix_backend.hpp b/libs/locale/src/boost/locale/posix/posix_backend.hpp new file mode 100644 index 0000000..e172fe4 --- /dev/null +++ b/libs/locale/src/boost/locale/posix/posix_backend.hpp @@ -0,0 +1,15 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_POSIX_LOCALIZATION_BACKEND_HPP +#define BOOST_LOCALE_IMPL_POSIX_LOCALIZATION_BACKEND_HPP +namespace boost { namespace locale { + class localization_backend; + namespace impl_posix { + localization_backend* create_localization_backend(); + } // namespace impl_posix +}} // namespace boost::locale +#endif diff --git a/libs/locale/src/boost/locale/shared/date_time.cpp b/libs/locale/src/boost/locale/shared/date_time.cpp new file mode 100644 index 0000000..1970fec --- /dev/null +++ b/libs/locale/src/boost/locale/shared/date_time.cpp @@ -0,0 +1,424 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +namespace boost { namespace locale { + + using namespace period; + + ///////////////////////// + // Calendar + //////////////////////// + + calendar::calendar(const std::locale& l, const std::string& zone) : + locale_(l), tz_(zone), impl_(std::use_facet(l).create_calendar()) + { + impl_->set_timezone(tz_); + } + + calendar::calendar(const std::string& zone) : + tz_(zone), impl_(std::use_facet(std::locale()).create_calendar()) + { + impl_->set_timezone(tz_); + } + + calendar::calendar(const std::locale& l) : + locale_(l), tz_(time_zone::global()), impl_(std::use_facet(l).create_calendar()) + { + impl_->set_timezone(tz_); + } + + calendar::calendar(std::ios_base& ios) : + locale_(ios.getloc()), tz_(ios_info::get(ios).time_zone()), + impl_(std::use_facet(locale_).create_calendar()) + { + impl_->set_timezone(tz_); + } + + calendar::calendar() : + tz_(time_zone::global()), impl_(std::use_facet(std::locale()).create_calendar()) + { + impl_->set_timezone(tz_); + } + + calendar::~calendar() = default; + + calendar::calendar(const calendar& other) : locale_(other.locale_), tz_(other.tz_), impl_(other.impl_->clone()) {} + + calendar& calendar::operator=(const calendar& other) + { + impl_.reset(other.impl_->clone()); + locale_ = other.locale_; + tz_ = other.tz_; + return *this; + } + + bool calendar::is_gregorian() const + { + return impl_->get_option(abstract_calendar::is_gregorian) != 0; + } + + std::string calendar::get_time_zone() const + { + return tz_; + } + + std::locale calendar::get_locale() const + { + return locale_; + } + + int calendar::minimum(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::absolute_minimum); + } + + int calendar::greatest_minimum(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::greatest_minimum); + } + + int calendar::maximum(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::absolute_maximum); + } + + int calendar::least_maximum(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::least_maximum); + } + + int calendar::first_day_of_week() const + { + return impl_->get_value(period::marks::first_day_of_week, abstract_calendar::current); + } + + bool calendar::operator==(const calendar& other) const + { + return impl_->same(other.impl_.get()); + } + + bool calendar::operator!=(const calendar& other) const + { + return !(*this == other); + } + + ////////////////////////////////// + // date_time + ///////////////// + + date_time::date_time() : impl_(std::use_facet(std::locale()).create_calendar()) + { + impl_->set_timezone(time_zone::global()); + } + + date_time::date_time(const date_time& other) : impl_(other.impl_->clone()) {} + + date_time::date_time(const date_time& other, const date_time_period_set& s) + { + impl_.reset(other.impl_->clone()); + for(unsigned i = 0; i < s.size(); i++) { + impl_->set_value(s[i].type.mark(), s[i].value); + } + impl_->normalize(); + } + + date_time& date_time::operator=(const date_time& other) + { + impl_.reset(other.impl_->clone()); + return *this; + } + + date_time::date_time(double t) : impl_(std::use_facet(std::locale()).create_calendar()) + { + impl_->set_timezone(time_zone::global()); + time(t); + } + + date_time::date_time(double t, const calendar& cal) : impl_(cal.impl_->clone()) + { + time(t); + } + + date_time::date_time(const calendar& cal) : impl_(cal.impl_->clone()) {} + + date_time::date_time(const date_time_period_set& s) : + impl_(std::use_facet(std::locale()).create_calendar()) + { + impl_->set_timezone(time_zone::global()); + for(unsigned i = 0; i < s.size(); i++) { + impl_->set_value(s[i].type.mark(), s[i].value); + } + impl_->normalize(); + } + date_time::date_time(const date_time_period_set& s, const calendar& cal) : impl_(cal.impl_->clone()) + { + for(unsigned i = 0; i < s.size(); i++) { + impl_->set_value(s[i].type.mark(), s[i].value); + } + impl_->normalize(); + } + + date_time& date_time::operator=(const date_time_period_set& s) + { + for(unsigned i = 0; i < s.size(); i++) + impl_->set_value(s[i].type.mark(), s[i].value); + impl_->normalize(); + return *this; + } + + void date_time::set(period_type f, int v) + { + impl_->set_value(f.mark(), v); + impl_->normalize(); + } + + int date_time::get(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::current); + } + + date_time date_time::operator+(const date_time_period& v) const + { + date_time tmp(*this); + tmp += v; + return tmp; + } + + date_time date_time::operator-(const date_time_period& v) const + { + date_time tmp(*this); + tmp -= v; + return tmp; + } + + date_time date_time::operator<<(const date_time_period& v) const + { + date_time tmp(*this); + tmp <<= v; + return tmp; + } + + date_time date_time::operator>>(const date_time_period& v) const + { + date_time tmp(*this); + tmp >>= v; + return tmp; + } + + date_time& date_time::operator+=(const date_time_period& v) + { + impl_->adjust_value(v.type.mark(), abstract_calendar::move, v.value); + return *this; + } + + date_time& date_time::operator-=(const date_time_period& v) + { + impl_->adjust_value(v.type.mark(), abstract_calendar::move, -v.value); + return *this; + } + + date_time& date_time::operator<<=(const date_time_period& v) + { + impl_->adjust_value(v.type.mark(), abstract_calendar::roll, v.value); + return *this; + } + + date_time& date_time::operator>>=(const date_time_period& v) + { + impl_->adjust_value(v.type.mark(), abstract_calendar::roll, -v.value); + return *this; + } + + date_time date_time::operator+(const date_time_period_set& v) const + { + date_time tmp(*this); + tmp += v; + return tmp; + } + + date_time date_time::operator-(const date_time_period_set& v) const + { + date_time tmp(*this); + tmp -= v; + return tmp; + } + + date_time date_time::operator<<(const date_time_period_set& v) const + { + date_time tmp(*this); + tmp <<= v; + return tmp; + } + + date_time date_time::operator>>(const date_time_period_set& v) const + { + date_time tmp(*this); + tmp >>= v; + return tmp; + } + + date_time& date_time::operator+=(const date_time_period_set& v) + { + for(unsigned i = 0; i < v.size(); i++) { + *this += v[i]; + } + return *this; + } + + date_time& date_time::operator-=(const date_time_period_set& v) + { + for(unsigned i = 0; i < v.size(); i++) { + *this -= v[i]; + } + return *this; + } + + date_time& date_time::operator<<=(const date_time_period_set& v) + { + for(unsigned i = 0; i < v.size(); i++) { + *this <<= v[i]; + } + return *this; + } + + date_time& date_time::operator>>=(const date_time_period_set& v) + { + for(unsigned i = 0; i < v.size(); i++) { + *this >>= v[i]; + } + return *this; + } + + double date_time::time() const + { + return impl_->get_time_ms() / 1000.; + } + + void date_time::time(double v) + { + double seconds; + const double fract_seconds = std::modf(v, &seconds); // v = seconds + fract_seconds + posix_time ptime; + ptime.seconds = static_cast(seconds); + int64_t nano = static_cast(fract_seconds * 1e9); + + constexpr int64_t ns_in_s = static_cast(1000) * 1000 * 1000; + if(seconds < 0 && nano != 0) { + assert(nano < 0); // Same sign + seconds -= 1; + nano = ns_in_s + nano; + } + if(nano < 0) + nano = 0; + else if(nano >= ns_in_s) + nano = ns_in_s - 1; + ptime.nanoseconds = static_cast(nano); + impl_->set_time(ptime); + } + + namespace { + int compare(const posix_time& left, const posix_time& right) + { + if(left.seconds < right.seconds) + return -1; + if(left.seconds > right.seconds) + return 1; + if(left.nanoseconds < right.nanoseconds) + return -1; + if(left.nanoseconds > right.nanoseconds) + return 1; + return 0; + } + } // namespace + + bool date_time::operator==(const date_time& other) const + { + return compare(impl_->get_time(), other.impl_->get_time()) == 0; + } + + bool date_time::operator!=(const date_time& other) const + { + return !(*this == other); + } + + bool date_time::operator<(const date_time& other) const + { + return compare(impl_->get_time(), other.impl_->get_time()) < 0; + } + + bool date_time::operator>=(const date_time& other) const + { + return !(*this < other); + } + + bool date_time::operator>(const date_time& other) const + { + return compare(impl_->get_time(), other.impl_->get_time()) > 0; + } + + bool date_time::operator<=(const date_time& other) const + { + return !(*this > other); + } + + void date_time::swap(date_time& other) + { + impl_.swap(other.impl_); + } + + int date_time::difference(const date_time& other, period_type f) const + { + return impl_->difference(*other.impl_.get(), f.mark()); + } + + int date_time::maximum(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::actual_maximum); + } + + int date_time::minimum(period_type f) const + { + return impl_->get_value(f.mark(), abstract_calendar::actual_minimum); + } + + bool date_time::is_in_daylight_saving_time() const + { + return impl_->get_option(abstract_calendar::is_dst) != 0; + } + + namespace time_zone { + boost::mutex& tz_mutex() + { + static boost::mutex m; + return m; + } + std::string& tz_id() + { + static std::string id; + return id; + } + std::string global() + { + boost::unique_lock lock(tz_mutex()); + std::string id = tz_id(); + return id; + } + std::string global(const std::string& new_id) + { + boost::unique_lock lock(tz_mutex()); + std::string id = tz_id(); + tz_id() = new_id; + return id; + } + } // namespace time_zone + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/shared/format.cpp b/libs/locale/src/boost/locale/shared/format.cpp new file mode 100644 index 0000000..8d3f740 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/format.cpp @@ -0,0 +1,166 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace detail { + struct format_parser::data { + unsigned position; + std::streamsize precision; + std::ios_base::fmtflags flags; + ios_info info; + std::locale saved_locale; + bool restore_locale; + void* cookie; + void (*imbuer)(void*, const std::locale&); + }; + + format_parser::format_parser(std::ios_base& ios, void* cookie, void (*imbuer)(void*, const std::locale&)) : + ios_(ios), d(new data) + { + d->position = std::numeric_limits::max(); + d->precision = ios.precision(); + d->flags = ios.flags(); + d->info = ios_info::get(ios); + d->saved_locale = ios.getloc(); + d->restore_locale = false; + d->cookie = cookie; + d->imbuer = imbuer; + } + + void format_parser::imbue(const std::locale& l) + { + d->imbuer(d->cookie, l); + } + + format_parser::~format_parser() = default; + + void format_parser::restore() + { + ios_info::get(ios_) = d->info; + ios_.width(0); + ios_.flags(d->flags); + if(d->restore_locale) + imbue(d->saved_locale); + } + + unsigned format_parser::get_position() + { + return d->position; + } + + void format_parser::set_one_flag(const std::string& key, const std::string& value) + { + if(key.empty()) + return; + unsigned i; + for(i = 0; i < key.size(); i++) { + if(key[i] < '0' || '9' < key[i]) + break; + } + if(i == key.size()) { + d->position = atoi(key.c_str()) - 1; + return; + } + + if(key == "num" || key == "number") { + as::number(ios_); + + if(value == "hex") + ios_.setf(std::ios_base::hex, std::ios_base::basefield); + else if(value == "oct") + ios_.setf(std::ios_base::oct, std::ios_base::basefield); + else if(value == "sci" || value == "scientific") + ios_.setf(std::ios_base::scientific, std::ios_base::floatfield); + else if(value == "fix" || value == "fixed") + ios_.setf(std::ios_base::fixed, std::ios_base::floatfield); + } else if(key == "cur" || key == "currency") { + as::currency(ios_); + if(value == "iso") + as::currency_iso(ios_); + else if(value == "nat" || value == "national") + as::currency_national(ios_); + } else if(key == "per" || key == "percent") { + as::percent(ios_); + } else if(key == "date") { + as::date(ios_); + if(value == "s" || value == "short") + as::date_short(ios_); + else if(value == "m" || value == "medium") + as::date_medium(ios_); + else if(value == "l" || value == "long") + as::date_long(ios_); + else if(value == "f" || value == "full") + as::date_full(ios_); + } else if(key == "time") { + as::time(ios_); + if(value == "s" || value == "short") + as::time_short(ios_); + else if(value == "m" || value == "medium") + as::time_medium(ios_); + else if(value == "l" || value == "long") + as::time_long(ios_); + else if(value == "f" || value == "full") + as::time_full(ios_); + } else if(key == "dt" || key == "datetime") { + as::datetime(ios_); + if(value == "s" || value == "short") { + as::date_short(ios_); + as::time_short(ios_); + } else if(value == "m" || value == "medium") { + as::date_medium(ios_); + as::time_medium(ios_); + } else if(value == "l" || value == "long") { + as::date_long(ios_); + as::time_long(ios_); + } else if(value == "f" || value == "full") { + as::date_full(ios_); + as::time_full(ios_); + } + } else if(key == "spell" || key == "spellout") { + as::spellout(ios_); + } else if(key == "ord" || key == "ordinal") { + as::ordinal(ios_); + } else if(key == "left" || key == "<") + ios_.setf(std::ios_base::left, std::ios_base::adjustfield); + else if(key == "right" || key == ">") + ios_.setf(std::ios_base::right, std::ios_base::adjustfield); + else if(key == "gmt") + as::gmt(ios_); + else if(key == "local") + as::local_time(ios_); + else if(key == "timezone" || key == "tz") + ios_info::get(ios_).time_zone(value); + else if(key == "w" || key == "width") + ios_.width(atoi(value.c_str())); + else if(key == "p" || key == "precision") + ios_.precision(atoi(value.c_str())); + else if(key == "locale") { + if(!d->restore_locale) { + d->saved_locale = ios_.getloc(); + d->restore_locale = true; + } + + std::string encoding = std::use_facet(d->saved_locale).encoding(); + generator gen; + gen.categories(category_t::formatting); + + std::locale new_loc; + if(value.find('.') == std::string::npos) + new_loc = gen(value + "." + encoding); + else + new_loc = gen(value); + + imbue(new_loc); + } + } +}}} // namespace boost::locale::detail +// boostinspect:nominmax diff --git a/libs/locale/src/boost/locale/shared/formatting.cpp b/libs/locale/src/boost/locale/shared/formatting.cpp new file mode 100644 index 0000000..5c1e5e7 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/formatting.cpp @@ -0,0 +1,131 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include "boost/locale/shared/ios_prop.hpp" +#include +#include + +namespace boost { namespace locale { + + ios_info::string_set::string_set() : type(0), size(0), ptr(0) {} + ios_info::string_set::~string_set() + { + delete[] ptr; + } + ios_info::string_set::string_set(const string_set& other) + { + if(other.ptr != 0) { + ptr = new char[other.size]; + size = other.size; + type = other.type; + memcpy(ptr, other.ptr, size); + } else { + ptr = 0; + size = 0; + type = 0; + } + } + + void ios_info::string_set::swap(string_set& other) + { + std::swap(type, other.type); + std::swap(size, other.size); + std::swap(ptr, other.ptr); + } + + ios_info::string_set& ios_info::string_set::operator=(string_set other) + { + swap(other); + return *this; + } + + ios_info::ios_info() : flags_(0), domain_id_(0), time_zone_(time_zone::global()) {} + + ios_info::~ios_info() = default; + + ios_info::ios_info(const ios_info&) = default; + ios_info& ios_info::operator=(const ios_info&) = default; + + void ios_info::display_flags(uint64_t f) + { + flags_ = (flags_ & ~uint64_t(flags::display_flags_mask)) | f; + } + uint64_t ios_info::display_flags() const + { + return flags_ & flags::display_flags_mask; + } + + void ios_info::currency_flags(uint64_t f) + { + flags_ = (flags_ & ~uint64_t(flags::currency_flags_mask)) | f; + } + uint64_t ios_info::currency_flags() const + { + return flags_ & flags::currency_flags_mask; + } + + void ios_info::date_flags(uint64_t f) + { + flags_ = (flags_ & ~uint64_t(flags::date_flags_mask)) | f; + } + uint64_t ios_info::date_flags() const + { + return flags_ & flags::date_flags_mask; + } + + void ios_info::time_flags(uint64_t f) + { + flags_ = (flags_ & ~uint64_t(flags::time_flags_mask)) | f; + } + uint64_t ios_info::time_flags() const + { + return flags_ & flags::time_flags_mask; + } + + void ios_info::domain_id(int id) + { + domain_id_ = id; + } + int ios_info::domain_id() const + { + return domain_id_; + } + + void ios_info::time_zone(const std::string& tz) + { + time_zone_ = tz; + } + std::string ios_info::time_zone() const + { + return time_zone_; + } + + const ios_info::string_set& ios_info::date_time_pattern_set() const + { + return datetime_; + } + + ios_info::string_set& ios_info::date_time_pattern_set() + { + return datetime_; + } + + ios_info& ios_info::get(std::ios_base& ios) + { + return impl::ios_prop::get(ios); + } + + void ios_info::on_imbue() {} + + namespace { + struct initializer { + initializer() { impl::ios_prop::global_init(); } + } initializer_instance; + } // namespace + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/shared/generator.cpp b/libs/locale/src/boost/locale/shared/generator.cpp new file mode 100644 index 0000000..fe4e20c --- /dev/null +++ b/libs/locale/src/boost/locale/shared/generator.cpp @@ -0,0 +1,192 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { + struct generator::data { + data(const localization_backend_manager& mgr) : + cats(all_categories), chars(all_characters), caching_enabled(false), use_ansi_encoding(false), + backend_manager(mgr) + {} + + typedef std::map cached_type; + mutable cached_type cached; + mutable boost::mutex cached_lock; + + category_t cats; + char_facet_t chars; + + bool caching_enabled; + bool use_ansi_encoding; + + std::vector paths; + std::vector domains; + + std::map> options; + + localization_backend_manager backend_manager; + }; + + generator::generator(const localization_backend_manager& mgr) : d(new generator::data(mgr)) {} + generator::generator() : d(new generator::data(localization_backend_manager::global())) {} + generator::~generator() = default; + + category_t generator::categories() const + { + return d->cats; + } + void generator::categories(category_t t) + { + d->cats = t; + } + + void generator::characters(char_facet_t t) + { + d->chars = t; + } + + char_facet_t generator::characters() const + { + return d->chars; + } + + void generator::add_messages_domain(const std::string& domain) + { + if(std::find(d->domains.begin(), d->domains.end(), domain) == d->domains.end()) + d->domains.push_back(domain); + } + + void generator::set_default_messages_domain(const std::string& domain) + { + std::vector::iterator p; + if((p = std::find(d->domains.begin(), d->domains.end(), domain)) != d->domains.end()) { + d->domains.erase(p); + } + d->domains.insert(d->domains.begin(), domain); + } + + void generator::clear_domains() + { + d->domains.clear(); + } + void generator::add_messages_path(const std::string& path) + { + d->paths.push_back(path); + } + void generator::clear_paths() + { + d->paths.clear(); + } + void generator::clear_cache() + { + d->cached.clear(); + } + + std::locale generator::generate(const std::string& id) const + { + std::locale base = std::locale::classic(); + + return generate(base, id); + } + + std::locale generator::generate(const std::locale& base, const std::string& id) const + { + if(d->caching_enabled) { + boost::unique_lock guard(d->cached_lock); + data::cached_type::const_iterator p = d->cached.find(id); + if(p != d->cached.end()) { + return p->second; + } + } + hold_ptr backend(d->backend_manager.create()); + set_all_options(*backend, id); + + std::locale result = base; + category_t facets = d->cats; + char_facet_t chars = d->chars; + + for(category_t facet = per_character_facet_first; facet <= per_character_facet_last; ++facet) { + if(!(facets & facet)) + continue; + for(char_facet_t ch = character_facet_first; ch <= character_facet_last; ++ch) { + if(ch & chars) + result = backend->install(result, facet, ch); + } + } + for(category_t facet = non_character_facet_first; facet <= non_character_facet_last; ++facet) { + if(facets & facet) + result = backend->install(result, facet, char_facet_t::nochar); + } + if(d->caching_enabled) { + boost::unique_lock guard(d->cached_lock); + data::cached_type::const_iterator p = d->cached.find(id); + if(p == d->cached.end()) { + d->cached[id] = result; + } + } + return result; + } + + bool generator::use_ansi_encoding() const + { + return d->use_ansi_encoding; + } + + void generator::use_ansi_encoding(bool v) + { + d->use_ansi_encoding = v; + } + + bool generator::locale_cache_enabled() const + { + return d->caching_enabled; + } + void generator::locale_cache_enabled(bool enabled) + { + d->caching_enabled = enabled; + } + + void generator::set_all_options(localization_backend& backend, const std::string& id) const + { + backend.set_option("locale", id); + if(d->use_ansi_encoding) + backend.set_option("use_ansi_encoding", "true"); + for(size_t i = 0; i < d->domains.size(); i++) + backend.set_option("message_application", d->domains[i]); + for(size_t i = 0; i < d->paths.size(); i++) + backend.set_option("message_path", d->paths[i]); + } + + // Sanity check + + static_assert((char_facet_t::char_f | char_facet_t::wchar_f) & char_facet_t::char_f, "!"); + static_assert((char_facet_t::char_f | char_facet_t::wchar_f) & char_facet_t::wchar_f, "!"); + static_assert(!((all_characters ^ char_facet_t::wchar_f) & char_facet_t::wchar_f), "!"); + + static_assert((category_t::calendar | category_t::convert) & category_t::calendar, "!"); + static_assert((category_t::calendar | category_t::convert) & category_t::convert, "!"); + static_assert(!((all_categories ^ category_t::calendar) & category_t::calendar), "!"); + +#ifndef BOOST_NO_CXX14_CONSTEXPR + template + constexpr T inc_enum(T v) + { + return ++v; + } + static_assert(inc_enum(char_facet_t::nochar) == char_facet_t::char_f, "!"); + static_assert(inc_enum(char_facet_t::char_f) == char_facet_t::wchar_f, "!"); + static_assert(inc_enum(category_t::convert) == category_t::collation, "!"); +#endif + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/shared/iconv_codecvt.cpp b/libs/locale/src/boost/locale/shared/iconv_codecvt.cpp new file mode 100644 index 0000000..764fe3c --- /dev/null +++ b/libs/locale/src/boost/locale/shared/iconv_codecvt.cpp @@ -0,0 +1,179 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2022-2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "iconv_codecvt.hpp" +#include +#include +#include +#include +#include +#ifdef BOOST_LOCALE_WITH_ICONV +# include "boost/locale/util/iconv.hpp" +#endif + +namespace boost { namespace locale { + +#ifdef BOOST_LOCALE_WITH_ICONV + static const char* utf32_encoding() + { + union { + char one; + uint32_t value; + } test; + test.value = 1; + return (test.one == 1) ? "UTF-32LE" : "UTF-32BE"; + } + + class mb2_iconv_converter : public util::base_converter { + public: + mb2_iconv_converter(const std::string& encoding) : encoding_(encoding) + { + iconv_handle d = iconv_open(utf32_encoding(), encoding.c_str()); + if(!d) + throw std::runtime_error("Unsupported encoding" + encoding); + + for(unsigned c = 0; c < first_byte_table_.size(); c++) { + const char ibuf[2] = {char(c), 0}; + size_t insize = sizeof(ibuf); + uint32_t obuf[2] = {illegal, illegal}; + size_t outsize = sizeof(obuf); + // Basic single codepoint conversion + call_iconv(d, ibuf, &insize, reinterpret_cast(obuf), &outsize); + if(insize == 0 && outsize == 0 && obuf[1] == 0) + first_byte_table_[c] = obuf[0]; + else { + // Test if this is illegal first byte or incomplete + insize = 1; + outsize = sizeof(obuf); + call_iconv(d, nullptr, nullptr, nullptr, nullptr); + size_t res = call_iconv(d, ibuf, &insize, reinterpret_cast(obuf), &outsize); + + // Now if this single byte starts a sequence we add incomplete + // to know to ask that we need two bytes, otherwise it may only be illegal + + first_byte_table_[c] = (res == size_t(-1) && errno == EINVAL) ? incomplete : illegal; + } + } + } + + mb2_iconv_converter(const mb2_iconv_converter& other) : + first_byte_table_(other.first_byte_table_), encoding_(other.encoding_) + {} + + bool is_thread_safe() const override { return false; } + + mb2_iconv_converter* clone() const override { return new mb2_iconv_converter(*this); } + + uint32_t to_unicode(const char*& begin, const char* end) override + { + if(begin == end) + return incomplete; + + const unsigned char seq0 = *begin; + +# if defined(BOOST_GCC_VERSION) && BOOST_GCC_VERSION >= 40600 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wtype-limits" +# endif + static_assert(std::numeric_limits::max() + < std::tuple_size::value, + "Wrong table size"); +# if defined(BOOST_GCC_VERSION) && BOOST_GCC_VERSION >= 40600 +# pragma GCC diagnostic pop +# endif + + const uint32_t index = first_byte_table_[seq0]; + if(index == illegal) + return illegal; + if(index != incomplete) { + begin++; + return index; + } else if(begin + 1 == end) + return incomplete; + + open(to_utf_, utf32_encoding(), encoding_.c_str()); + + // maybe illegal or may be double byte + + const char inseq[3] = {static_cast(seq0), begin[1], 0}; + size_t insize = sizeof(inseq); + uint32_t result[2] = {illegal, illegal}; + size_t outsize = sizeof(result); + call_iconv(to_utf_, inseq, &insize, reinterpret_cast(result), &outsize); + if(outsize == 0 && insize == 0 && result[1] == 0) { + begin += 2; + return result[0]; + } + return illegal; + } + + uint32_t from_unicode(uint32_t cp, char* begin, const char* end) override + { + if(cp == 0) { + if(begin != end) { + *begin = 0; + return 1; + } else { + return incomplete; + } + } + + open(from_utf_, encoding_.c_str(), utf32_encoding()); + + const uint32_t inbuf[2] = {cp, 0}; + size_t insize = sizeof(inbuf); + char outseq[3] = {0}; + size_t outsize = 3; + + call_iconv(from_utf_, reinterpret_cast(inbuf), &insize, outseq, &outsize); + + if(insize != 0 || outsize > 1) + return illegal; + size_t len = 2 - outsize; + size_t reminder = end - begin; + if(reminder < len) + return incomplete; + for(unsigned i = 0; i < len; i++) + *begin++ = outseq[i]; + return static_cast(len); + } + + int max_len() const override + { + return 2; + } + + private: + std::array first_byte_table_; + std::string encoding_; + iconv_handle to_utf_, from_utf_; + + static void open(iconv_handle& d, const char* to, const char* from) + { + if(!d) + d = iconv_open(to, from); + BOOST_ASSERT(d); + } + }; + + std::unique_ptr create_iconv_converter(const std::string& encoding) + { + try { + return std::unique_ptr(new mb2_iconv_converter(encoding)); + } catch(const std::exception&) { + return nullptr; + } + } + +#else // no iconv + std::unique_ptr create_iconv_converter(const std::string& /*encoding*/) + { + return nullptr; + } +#endif + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/shared/iconv_codecvt.hpp b/libs/locale/src/boost/locale/shared/iconv_codecvt.hpp new file mode 100644 index 0000000..51ddc71 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/iconv_codecvt.hpp @@ -0,0 +1,20 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_ICONV_CODECVT_HPP +#define BOOST_LOCALE_ICONV_CODECVT_HPP + +#include +#include +#include + +namespace boost { namespace locale { + BOOST_LOCALE_DECL + std::unique_ptr create_iconv_converter(const std::string& encoding); + +}} // namespace boost::locale + +#endif diff --git a/libs/locale/src/boost/locale/shared/ids.cpp b/libs/locale/src/boost/locale/shared/ids.cpp new file mode 100644 index 0000000..30d91d7 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/ids.cpp @@ -0,0 +1,108 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { + + std::locale::id info::id; + // Make sure we have the VTable here (Export/Import issues) + info::~info() = default; + + std::locale::id calendar_facet::id; + calendar_facet::~calendar_facet() = default; + + abstract_calendar::~abstract_calendar() = default; + + std::locale::id converter::id; + converter::~converter() = default; + std::locale::id base_message_format::id; + base_message_format::~base_message_format() = default; + + std::locale::id converter::id; + converter::~converter() = default; + std::locale::id base_message_format::id; + base_message_format::~base_message_format() = default; + +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + + std::locale::id converter::id; + converter::~converter() = default; + std::locale::id base_message_format::id; + base_message_format::~base_message_format() = default; + +#endif + +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + + std::locale::id converter::id; + converter::~converter() = default; + std::locale::id base_message_format::id; + base_message_format::~base_message_format() = default; + +#endif + + namespace boundary { + + std::locale::id boundary_indexing::id; + boundary_indexing::~boundary_indexing() = default; + + std::locale::id boundary_indexing::id; + boundary_indexing::~boundary_indexing() = default; + +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + std::locale::id boundary_indexing::id; + boundary_indexing::~boundary_indexing() = default; +#endif + +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + std::locale::id boundary_indexing::id; + boundary_indexing::~boundary_indexing() = default; +#endif + } // namespace boundary + + namespace { + // Initialize each facet once to avoid issues where doing so + // in a multithreaded environment could cause problems (races) + struct init_all { + init_all() + { + const std::locale& l = std::locale::classic(); + init_by(l); + init_by(l); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + init_by(l); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + init_by(l); +#endif + + init_facet(l); + init_facet(l); + } + template + void init_by(const std::locale& l) + { + init_facet>(l); + init_facet>(l); + init_facet>(l); + } + template + void init_facet(const std::locale& l) + { + // Use the facet to initialize e.g. their std::locale::id + ignore_unused(std::has_facet(l)); + } + } facet_initializer; + } // namespace + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/shared/ios_prop.hpp b/libs/locale/src/boost/locale/shared/ios_prop.hpp new file mode 100644 index 0000000..d825659 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/ios_prop.hpp @@ -0,0 +1,65 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2022 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_SRC_LOCALE_IOS_PROP_HPP +#define BOOST_SRC_LOCALE_IOS_PROP_HPP + +#include +#include +#include + +namespace boost { namespace locale { namespace impl { + + template + class BOOST_SYMBOL_VISIBLE ios_prop { + public: + static Property& get(std::ios_base& ios) + { + Property* result = get_impl(ios); + return result ? *result : create(ios); + } + + static void global_init() { get_id(); } + + private: + static Property* get_impl(std::ios_base& ios) { return static_cast(ios.pword(get_id())); } + + static Property& create(std::ios_base& ios) + { + BOOST_ASSERT_MSG(!get_impl(ios), "Called create while the property already exists"); + const int id = get_id(); + ios.register_callback(callback, id); + Property* value = new Property(); + ios.pword(id) = value; + return *value; + } + + static void callback(std::ios_base::event ev, std::ios_base& ios, int id) + { + Property* prop = get_impl(ios); + if(!prop) + return; + switch(ev) { + case std::ios_base::erase_event: + delete prop; + ios.pword(id) = nullptr; + break; + case std::ios_base::copyfmt_event: ios.pword(id) = new Property(*prop); break; + case std::ios_base::imbue_event: prop->on_imbue(); break; + default: break; + } + } + static int get_id() + { + static int id = std::ios_base::xalloc(); + return id; + } + }; + +}}} // namespace boost::locale::impl + +#endif diff --git a/libs/locale/src/boost/locale/shared/localization_backend.cpp b/libs/locale/src/boost/locale/shared/localization_backend.cpp new file mode 100644 index 0000000..e15de17 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/localization_backend.cpp @@ -0,0 +1,249 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_LOCALE_WITH_ICU +# include "boost/locale/icu/icu_backend.hpp" +#endif + +#ifndef BOOST_LOCALE_NO_POSIX_BACKEND +# include "boost/locale/posix/posix_backend.hpp" +#endif + +#ifndef BOOST_LOCALE_NO_STD_BACKEND +# include "boost/locale/std/std_backend.hpp" +#endif + +#ifndef BOOST_LOCALE_NO_WINAPI_BACKEND +# include "boost/locale/win32/win_backend.hpp" +#endif + +namespace boost { namespace locale { + localization_backend::~localization_backend() = default; + + class localization_backend_manager::impl { + public: + impl(const impl& other) : default_backends_(other.default_backends_) + { + for(all_backends_type::const_iterator p = other.all_backends_.begin(); p != other.all_backends_.end(); ++p) + { + all_backends_type::value_type v; + v.first = p->first; + v.second.reset(p->second->clone()); + all_backends_.push_back(v); + } + } + impl() : default_backends_(32, -1) {} + + impl& operator=(const impl&) = delete; + + localization_backend* create() const + { + std::vector> backends; + for(unsigned i = 0; i < all_backends_.size(); i++) + backends.push_back(all_backends_[i].second); + return new actual_backend(backends, default_backends_); + } + void adopt_backend(const std::string& name, localization_backend* backend_ptr) + { + std::shared_ptr sptr(backend_ptr); + if(all_backends_.empty()) { + all_backends_.push_back(std::make_pair(name, sptr)); + for(unsigned i = 0; i < default_backends_.size(); i++) + default_backends_[i] = 0; + } else { + for(unsigned i = 0; i < all_backends_.size(); i++) + if(all_backends_[i].first == name) + return; + all_backends_.push_back(std::make_pair(name, sptr)); + } + } + + void select(const std::string& backend_name, category_t category = all_categories) + { + unsigned id; + for(id = 0; id < all_backends_.size(); ++id) { + if(all_backends_[id].first == backend_name) + break; + } + if(id == all_backends_.size()) + return; + category_t flag = category_first; + for(unsigned i = 0; i < default_backends_.size(); ++flag, ++i) { + if(category & flag) { + default_backends_[i] = id; + } + } + } + + void remove_all_backends() + { + all_backends_.clear(); + for(unsigned i = 0; i < default_backends_.size(); i++) { + default_backends_[i] = -1; + } + } + std::vector get_all_backends() const + { + std::vector res; + all_backends_type::const_iterator p; + for(p = all_backends_.begin(); p != all_backends_.end(); ++p) { + res.push_back(p->first); + } + return res; + } + + private: + class actual_backend : public localization_backend { + public: + actual_backend(const std::vector>& backends, + const std::vector& index) : + index_(index) + { + backends_.resize(backends.size()); + for(unsigned i = 0; i < backends.size(); i++) { + backends_[i].reset(backends[i]->clone()); + } + } + actual_backend* clone() const override { return new actual_backend(backends_, index_); } + void set_option(const std::string& name, const std::string& value) override + { + for(unsigned i = 0; i < backends_.size(); i++) + backends_[i]->set_option(name, value); + } + void clear_options() override + { + for(unsigned i = 0; i < backends_.size(); i++) + backends_[i]->clear_options(); + } + std::locale install(const std::locale& l, category_t category, char_facet_t type) override + { + unsigned id = 0; + for(category_t v = category_first; v != category; ++v, ++id) { + if(v == category_last) + return l; + } + if(id >= index_.size() || index_[id] == -1) + return l; + return backends_[index_[id]]->install(l, category, type); + } + + private: + std::vector> backends_; + std::vector index_; + }; + + typedef std::vector>> all_backends_type; + all_backends_type all_backends_; + std::vector default_backends_; + }; + + localization_backend_manager::localization_backend_manager() : pimpl_(new impl()) {} + + localization_backend_manager::~localization_backend_manager() = default; + + localization_backend_manager::localization_backend_manager(const localization_backend_manager& other) : + pimpl_(new impl(*other.pimpl_)) + {} + + localization_backend_manager& localization_backend_manager::operator=(const localization_backend_manager& other) + { + pimpl_.reset(new impl(*other.pimpl_)); + return *this; + } + + std::unique_ptr localization_backend_manager::get() const + { + return std::unique_ptr(pimpl_->create()); + } + void localization_backend_manager::add_backend(const std::string& name, + std::unique_ptr backend) + { + pimpl_->adopt_backend(name, backend.release()); + } + + localization_backend* localization_backend_manager::create() const + { + return pimpl_->create(); + } + void localization_backend_manager::adopt_backend(const std::string& name, localization_backend* backend) + { + pimpl_->adopt_backend(name, backend); + } + + void localization_backend_manager::remove_all_backends() + { + pimpl_->remove_all_backends(); + } + std::vector localization_backend_manager::get_all_backends() const + { + return pimpl_->get_all_backends(); + } + void localization_backend_manager::select(const std::string& backend_name, category_t category) + { + pimpl_->select(backend_name, category); + } + + namespace { + // prevent initialization order fiasco + boost::mutex& localization_backend_manager_mutex() + { + static boost::mutex the_mutex; + return the_mutex; + } + // prevent initialization order fiasco + localization_backend_manager& localization_backend_manager_global() + { + static localization_backend_manager the_manager; + return the_manager; + } + + struct init { + init() + { + localization_backend_manager mgr; +#ifdef BOOST_LOCALE_WITH_ICU + mgr.adopt_backend("icu", impl_icu::create_localization_backend()); +#endif + +#ifndef BOOST_LOCALE_NO_POSIX_BACKEND + mgr.adopt_backend("posix", impl_posix::create_localization_backend()); +#endif + +#ifndef BOOST_LOCALE_NO_WINAPI_BACKEND + mgr.adopt_backend("winapi", impl_win::create_localization_backend()); +#endif + +#ifndef BOOST_LOCALE_NO_STD_BACKEND + mgr.adopt_backend("std", impl_std::create_localization_backend()); +#endif + + localization_backend_manager::global(mgr); + } + } do_init; + } // namespace + + localization_backend_manager localization_backend_manager::global() + { + boost::unique_lock lock(localization_backend_manager_mutex()); + localization_backend_manager mgr = localization_backend_manager_global(); + return mgr; + } + localization_backend_manager localization_backend_manager::global(const localization_backend_manager& in) + { + boost::unique_lock lock(localization_backend_manager_mutex()); + localization_backend_manager mgr = localization_backend_manager_global(); + localization_backend_manager_global() = in; + return mgr; + } + +}} // namespace boost::locale diff --git a/libs/locale/src/boost/locale/shared/message.cpp b/libs/locale/src/boost/locale/shared/message.cpp new file mode 100644 index 0000000..424e77a --- /dev/null +++ b/libs/locale/src/boost/locale/shared/message.cpp @@ -0,0 +1,678 @@ +// +// Copyright (c) 2009-2015 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_DETAIL_NO_CONTAINER_FWD + +// Need _wfopen which is an extension on MinGW but not on MinGW-w64 +// So remove the strict-mode define on (only) MinGW before including anything +#if defined(__MINGW32__) && defined(__STRICT_ANSI__) +# include <_mingw.h> +# ifndef __MINGW64_VERSION_MAJOR +# undef __STRICT_ANSI__ +# endif +#endif + +#include +#include +#include +#include +#include "boost/locale/shared/mo_hash.hpp" +#include "boost/locale/shared/mo_lambda.hpp" +#include "boost/locale/util/encoding.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace gnu_gettext { + + class c_file { + c_file(const c_file&); + void operator=(const c_file&); + + public: + FILE* file; + + c_file() : file(0) {} + ~c_file() { close(); } + + void close() + { + if(file) { + fclose(file); + file = 0; + } + } + +#if defined(BOOST_WINDOWS) + + bool open(const std::string& file_name, const std::string& encoding) + { + close(); + + // Under windows we have to use "_wfopen" to get + // access to path's with Unicode in them + // + // As not all standard C++ libraries support nonstandard std::istream::open(wchar_t const *) + // we would use old and good stdio and _wfopen CRTL functions + + std::wstring wfile_name = conv::to_utf(file_name, encoding); + file = _wfopen(wfile_name.c_str(), L"rb"); + + return file != 0; + } + +#else // POSIX systems do not have all this Wide API crap, as native codepages are UTF-8 + + // We do not use encoding as we use native file name encoding + + bool open(const std::string& file_name, const std::string& /* encoding */) + { + close(); + + file = fopen(file_name.c_str(), "rb"); + + return file != 0; + } + +#endif + }; + + class mo_file { + public: + typedef std::pair pair_type; + + mo_file(std::vector& file) : native_byteorder_(true), size_(0) + { + load_file(file); + init(); + } + + mo_file(FILE* file) : native_byteorder_(true), size_(0) + { + load_file(file); + init(); + } + + pair_type find(const char* context_in, const char* key_in) const + { + pair_type null_pair((const char*)0, (const char*)0); + if(hash_size_ == 0) + return null_pair; + uint32_t hkey = 0; + if(context_in == 0) + hkey = pj_winberger_hash_function(key_in); + else { + pj_winberger_hash::state_type st = pj_winberger_hash::initial_state; + st = pj_winberger_hash::update_state(st, context_in); + st = pj_winberger_hash::update_state(st, '\4'); // EOT + st = pj_winberger_hash::update_state(st, key_in); + hkey = st; + } + uint32_t incr = 1 + hkey % (hash_size_ - 2); + hkey %= hash_size_; + uint32_t orig = hkey; + + do { + uint32_t idx = get(hash_offset_ + 4 * hkey); + /// Not found + if(idx == 0) + return null_pair; + /// If equal values return translation + if(key_equals(key(idx - 1), context_in, key_in)) + return value(idx - 1); + /// Rehash + hkey = (hkey + incr) % hash_size_; + } while(hkey != orig); + return null_pair; + } + + static bool key_equals(const char* real_key, const char* cntx, const char* key) + { + if(cntx == 0) + return strcmp(real_key, key) == 0; + else { + size_t real_len = strlen(real_key); + size_t cntx_len = strlen(cntx); + size_t key_len = strlen(key); + if(cntx_len + 1 + key_len != real_len) + return false; + return memcmp(real_key, cntx, cntx_len) == 0 && real_key[cntx_len] == '\4' + && memcmp(real_key + cntx_len + 1, key, key_len) == 0; + } + } + + const char* key(int id) const + { + uint32_t off = get(keys_offset_ + id * 8 + 4); + return data_ + off; + } + + pair_type value(int id) const + { + uint32_t len = get(translations_offset_ + id * 8); + uint32_t off = get(translations_offset_ + id * 8 + 4); + if(off >= file_size_ || off + len >= file_size_) + throw std::runtime_error("Bad mo-file format"); + return pair_type(&data_[off], &data_[off] + len); + } + + bool has_hash() const { return hash_size_ != 0; } + + size_t size() const { return size_; } + + bool empty() { return size_ == 0; } + + private: + void init() + { + // Read all format sizes + size_ = get(8); + keys_offset_ = get(12); + translations_offset_ = get(16); + hash_size_ = get(20); + hash_offset_ = get(24); + } + + void load_file(std::vector& data) + { + vdata_.swap(data); + file_size_ = vdata_.size(); + data_ = &vdata_[0]; + if(file_size_ < 4) + throw std::runtime_error("invalid 'mo' file format - the file is too short"); + uint32_t magic = 0; + memcpy(&magic, data_, 4); + if(magic == 0x950412de) + native_byteorder_ = true; + else if(magic == 0xde120495) + native_byteorder_ = false; + else + throw std::runtime_error("Invalid file format - invalid magic number"); + } + + void load_file(FILE* file) + { + uint32_t magic = 0; + // if the size is wrong magic would be wrong + // ok to ignore fread result + size_t four_bytes = fread(&magic, 4, 1, file); + (void)four_bytes; // shut GCC + + if(magic == 0x950412de) + native_byteorder_ = true; + else if(magic == 0xde120495) + native_byteorder_ = false; + else + throw std::runtime_error("Invalid file format"); + + fseek(file, 0, SEEK_END); + long len = ftell(file); + if(len < 0) { + throw std::runtime_error("Wrong file object"); + } + fseek(file, 0, SEEK_SET); + vdata_.resize(len + 1, 0); // +1 to make sure the vector is not empty + if(fread(&vdata_.front(), 1, len, file) != unsigned(len)) + throw std::runtime_error("Failed to read file"); + data_ = &vdata_[0]; + file_size_ = len; + } + + uint32_t get(unsigned offset) const + { + uint32_t tmp; + if(offset > file_size_ - 4) { + throw std::runtime_error("Bad mo-file format"); + } + memcpy(&tmp, data_ + offset, 4); + convert(tmp); + return tmp; + } + + void convert(uint32_t& v) const + { + if(native_byteorder_) + return; + v = ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24); + } + + uint32_t keys_offset_; + uint32_t translations_offset_; + uint32_t hash_size_; + uint32_t hash_offset_; + + const char* data_; + size_t file_size_; + std::vector vdata_; + bool native_byteorder_; + size_t size_; + }; + + template + struct mo_file_use_traits { + static constexpr bool in_use = false; + typedef CharType char_type; + typedef std::pair pair_type; + static pair_type use(const mo_file& /*mo*/, const char_type* /*context*/, const char_type* /*key*/) + { + return pair_type((const char_type*)(0), (const char_type*)(0)); + } + }; + + template<> + struct mo_file_use_traits { + static constexpr bool in_use = true; + typedef char char_type; + typedef std::pair pair_type; + static pair_type use(const mo_file& mo, const char* context, const char* key) { return mo.find(context, key); } + }; + + template + class converter { + public: + converter(std::string /*out_enc*/, std::string in_enc) : in_(in_enc) {} + + std::basic_string operator()(const char* begin, const char* end) + { + return conv::to_utf(begin, end, in_, conv::stop); + } + + private: + std::string in_; + }; + + template<> + class converter { + public: + converter(std::string out_enc, std::string in_enc) : out_(out_enc), in_(in_enc) {} + + std::string operator()(const char* begin, const char* end) + { + return conv::between(begin, end, out_, in_, conv::stop); + } + + private: + std::string out_, in_; + }; + + template + struct message_key { + typedef CharType char_type; + typedef std::basic_string string_type; + + message_key(const string_type& c = string_type()) : c_context_(0), c_key_(0) + { + size_t pos = c.find(char_type(4)); + if(pos == string_type::npos) { + key_ = c; + } else { + context_ = c.substr(0, pos); + key_ = c.substr(pos + 1); + } + } + message_key(const char_type* c, const char_type* k) : c_key_(k) + { + static const char_type empty = 0; + if(c != 0) + c_context_ = c; + else + c_context_ = ∅ + } + bool operator<(const message_key& other) const + { + int cc = compare(context(), other.context()); + if(cc != 0) + return cc < 0; + return compare(key(), other.key()) < 0; + } + bool operator==(const message_key& other) const + { + return compare(context(), other.context()) == 0 && compare(key(), other.key()) == 0; + } + bool operator!=(const message_key& other) const { return !(*this == other); } + const char_type* context() const + { + if(c_context_) + return c_context_; + return context_.c_str(); + } + const char_type* key() const + { + if(c_key_) + return c_key_; + return key_.c_str(); + } + + private: + static int compare(const char_type* l, const char_type* r) + { + typedef std::char_traits traits_type; + for(;;) { + char_type cl = *l++; + char_type cr = *r++; + if(cl == 0 && cr == 0) + return 0; + if(traits_type::lt(cl, cr)) + return -1; + if(traits_type::lt(cr, cl)) + return 1; + } + } + string_type context_; + string_type key_; + const char_type* c_context_; + const char_type* c_key_; + }; + + template + struct hash_function { + size_t operator()(const message_key& msg) const + { + pj_winberger_hash::state_type state = pj_winberger_hash::initial_state; + const CharType* p = msg.context(); + if(*p != 0) { + const CharType* e = util::str_end(p); + state = pj_winberger_hash::update_state(state, + reinterpret_cast(p), + reinterpret_cast(e)); + state = pj_winberger_hash::update_state(state, '\4'); + } + p = msg.key(); + const CharType* e = util::str_end(p); + state = pj_winberger_hash::update_state(state, + reinterpret_cast(p), + reinterpret_cast(e)); + return state; + } + }; + + // By default for wide types the conversion is not required + template + const CharType* runtime_conversion(const CharType* msg, + std::basic_string& /*buffer*/, + bool /*do_conversion*/, + const std::string& /*locale_encoding*/, + const std::string& /*key_encoding*/) + { + return msg; + } + + // But still need to specialize for char + template<> + const char* runtime_conversion(const char* msg, + std::string& buffer, + bool do_conversion, + const std::string& locale_encoding, + const std::string& key_encoding) + { + if(!do_conversion) + return msg; + if(detail::is_us_ascii_string(msg)) + return msg; + std::string tmp = conv::between(msg, locale_encoding, key_encoding, conv::skip); + buffer.swap(tmp); + return buffer.c_str(); + } + + template + class mo_message : public message_format { + typedef CharType char_type; + typedef std::basic_string string_type; + typedef message_key key_type; + typedef std::unordered_map> catalog_type; + typedef std::vector catalogs_set_type; + typedef std::map domains_map_type; + + public: + typedef std::pair pair_type; + + const char_type* get(int domain_id, const char_type* context, const char_type* in_id) const override + { + return get_string(domain_id, context, in_id).first; + } + + const char_type* get(int domain_id, const char_type* context, const char_type* single_id, int n) const override + { + pair_type ptr = get_string(domain_id, context, single_id); + if(!ptr.first) + return nullptr; + int form = 0; + if(plural_forms_.at(domain_id)) + form = (*plural_forms_[domain_id])(n); + else + form = n == 1 ? 0 : 1; // Fallback to English plural form + + const CharType* p = ptr.first; + for(int i = 0; p < ptr.second && i < form; i++) { + p = std::find(p, ptr.second, CharType(0)); + if(p == ptr.second) + return nullptr; + ++p; + } + if(p >= ptr.second) + return nullptr; + return p; + } + + int domain(const std::string& domain) const override + { + domains_map_type::const_iterator p = domains_.find(domain); + if(p == domains_.end()) + return -1; + return p->second; + } + + mo_message(const messages_info& inf) : key_conversion_required_(false) + { + std::string language = inf.language; + std::string variant = inf.variant; + std::string country = inf.country; + std::string encoding = inf.encoding; + std::string lc_cat = inf.locale_category; + const std::vector& domains = inf.domains; + const std::vector& search_paths = inf.paths; + + // List of fallbacks: en_US@euro, en@euro, en_US, en. + std::vector paths; + + if(!variant.empty() && !country.empty()) + paths.push_back(language + "_" + country + "@" + variant); + + if(!variant.empty()) + paths.push_back(language + "@" + variant); + + if(!country.empty()) + paths.push_back(language + "_" + country); + + paths.push_back(language); + + catalogs_.resize(domains.size()); + mo_catalogs_.resize(domains.size()); + plural_forms_.resize(domains.size()); + + for(unsigned i = 0; i < domains.size(); i++) { + std::string domain = domains[i].name; + std::string key_encoding = domains[i].encoding; + domains_[domain] = i; + + bool found = false; + for(unsigned j = 0; !found && j < paths.size(); j++) { + for(unsigned k = 0; !found && k < search_paths.size(); k++) { + std::string full_path = search_paths[k] + "/" + paths[j] + "/" + lc_cat + "/" + domain + ".mo"; + found = load_file(full_path, encoding, key_encoding, i, inf.callback); + } + } + } + } + + const char_type* convert(const char_type* msg, string_type& buffer) const override + { + return runtime_conversion(msg, + buffer, + key_conversion_required_, + locale_encoding_, + key_encoding_); + } + + private: + bool load_file(const std::string& file_name, + const std::string& locale_encoding, + const std::string& key_encoding, + int idx, + const messages_info::callback_type& callback) + { + locale_encoding_ = locale_encoding; + key_encoding_ = key_encoding; + + key_conversion_required_ = + sizeof(CharType) == 1 && !util::are_encodings_equal(locale_encoding, key_encoding); + + std::shared_ptr mo; + + if(callback) { + std::vector vfile = callback(file_name, locale_encoding); + if(vfile.empty()) + return false; + mo.reset(new mo_file(vfile)); + } else { + c_file the_file; + the_file.open(file_name, locale_encoding); + if(!the_file.file) + return false; + mo.reset(new mo_file(the_file.file)); + } + + std::string plural = extract(mo->value(0).first, "plural=", "\r\n;"); + + std::string mo_encoding = extract(mo->value(0).first, "charset=", " \r\n;"); + + if(mo_encoding.empty()) + throw std::runtime_error("Invalid mo-format, encoding is not specified"); + + if(!plural.empty()) + plural_forms_[idx] = lambda::compile(plural.c_str()); + + if(mo_useable_directly(mo_encoding, *mo)) + mo_catalogs_[idx] = mo; + else { + converter cvt_value(locale_encoding, mo_encoding); + converter cvt_key(key_encoding, mo_encoding); + for(unsigned i = 0; i < mo->size(); i++) { + const char* ckey = mo->key(i); + string_type skey = cvt_key(ckey, ckey + strlen(ckey)); + key_type key(skey); + + mo_file::pair_type tmp = mo->value(i); + string_type value = cvt_value(tmp.first, tmp.second); + catalogs_[idx][key].swap(value); + } + } + return true; + } + + // Check if the mo file as-is is useful + // 1. It is char and not wide character + // 2. The locale encoding and mo encoding is same + // 3. The source strings encoding and mo encoding is same or all + // mo key strings are US-ASCII + bool mo_useable_directly(const std::string& mo_encoding, const mo_file& mo) + { + BOOST_LOCALE_START_CONST_CONDITION + if(sizeof(CharType) != 1) + return false; + BOOST_LOCALE_END_CONST_CONDITION + if(!mo.has_hash()) + return false; + if(!util::are_encodings_equal(mo_encoding, locale_encoding_)) + return false; + if(util::are_encodings_equal(mo_encoding, key_encoding_)) { + return true; + } + for(unsigned i = 0; i < mo.size(); i++) { + if(!detail::is_us_ascii_string(mo.key(i))) { + return false; + } + } + return true; + } + + static std::string extract(const std::string& meta, const std::string& key, const char* separator) + { + size_t pos = meta.find(key); + if(pos == std::string::npos) + return ""; + pos += key.size(); /// size of charset= + size_t end_pos = meta.find_first_of(separator, pos); + return meta.substr(pos, end_pos - pos); + } + + pair_type get_string(int domain_id, const char_type* context, const char_type* in_id) const + { + pair_type null_pair((const CharType*)0, (const CharType*)0); + if(domain_id < 0 || size_t(domain_id) >= catalogs_.size()) + return null_pair; + BOOST_LOCALE_START_CONST_CONDITION + if(mo_file_use_traits::in_use && mo_catalogs_[domain_id]) { + BOOST_LOCALE_END_CONST_CONDITION + return mo_file_use_traits::use(*mo_catalogs_[domain_id], context, in_id); + } else { + key_type key(context, in_id); + const catalog_type& cat = catalogs_[domain_id]; + typename catalog_type::const_iterator p = cat.find(key); + if(p == cat.end()) { + return null_pair; + } + return pair_type(p->second.data(), p->second.data() + p->second.size()); + } + } + + catalogs_set_type catalogs_; + std::vector> mo_catalogs_; + std::vector> plural_forms_; + domains_map_type domains_; + + std::string locale_encoding_; + std::string key_encoding_; + bool key_conversion_required_; + }; + + template<> + message_format* create_messages_facet(const messages_info& info) + { + return new mo_message(info); + } + + template<> + message_format* create_messages_facet(const messages_info& info) + { + return new mo_message(info); + } + +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + + template<> + message_format* create_messages_facet(const messages_info& info) + { + return new mo_message(info); + } +#endif + +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + + template<> + message_format* create_messages_facet(const messages_info& info) + { + return new mo_message(info); + } +#endif + +}}} // namespace boost::locale::gnu_gettext diff --git a/libs/locale/src/boost/locale/shared/mo_hash.hpp b/libs/locale/src/boost/locale/shared/mo_hash.hpp new file mode 100644 index 0000000..ad41764 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/mo_hash.hpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +namespace boost { namespace locale { namespace gnu_gettext { + + struct pj_winberger_hash { + typedef uint32_t state_type; + + static constexpr state_type initial_state = 0; + + static state_type update_state(state_type value, char c) + { + value = (value << 4) + static_cast(c); + uint32_t high = (value & 0xF0000000U); + if(high != 0) + value = (value ^ (high >> 24)) ^ high; + return value; + } + static state_type update_state(state_type value, const char* ptr) + { + while(*ptr) + value = update_state(value, *ptr++); + return value; + } + static state_type update_state(state_type value, const char* begin, const char* end) + { + while(begin != end) + value = update_state(value, *begin++); + return value; + } + }; + + inline pj_winberger_hash::state_type pj_winberger_hash_function(const char* ptr) + { + pj_winberger_hash::state_type state = pj_winberger_hash::initial_state; + state = pj_winberger_hash::update_state(state, ptr); + return state; + } + + inline pj_winberger_hash::state_type pj_winberger_hash_function(const char* begin, const char* end) + { + pj_winberger_hash::state_type state = pj_winberger_hash::initial_state; + state = pj_winberger_hash::update_state(state, begin, end); + return state; + } +}}} // namespace boost::locale::gnu_gettext diff --git a/libs/locale/src/boost/locale/shared/mo_lambda.cpp b/libs/locale/src/boost/locale/shared/mo_lambda.cpp new file mode 100644 index 0000000..90d0c23 --- /dev/null +++ b/libs/locale/src/boost/locale/shared/mo_lambda.cpp @@ -0,0 +1,373 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/shared/mo_lambda.hpp" +#include +#include + +#ifdef BOOST_MSVC +# pragma warning(disable : 4512) // assignment operator could not be generated +#endif + +namespace boost { namespace locale { namespace gnu_gettext { namespace lambda { + + namespace { // anon + struct identity : public plural { + int operator()(int n) const override { return n; }; + identity* clone() const override { return new identity(); } + }; + + struct unary : public plural { + unary(plural_ptr ptr) : op1(ptr) {} + + protected: + plural_ptr op1; + }; + + struct binary : public plural { + binary(plural_ptr p1, plural_ptr p2) : op1(p1), op2(p2) {} + + protected: + plural_ptr op1, op2; + }; + + struct number : public plural { + number(int v) : val(v) {} + int operator()(int /*n*/) const override { return val; } + number* clone() const override { return new number(val); } + + private: + int val; + }; + +#define UNOP(name, oper) \ + struct name : public unary { \ + name(plural_ptr op) : unary(op) {} \ + int operator()(int n) const override { return oper(*op1)(n); } \ + name* clone() const override \ + { \ + plural_ptr op1_copy(op1->clone()); \ + return new name(op1_copy); \ + } \ + }; + +#define BINOP(name, oper) \ + struct name : public binary { \ + name(plural_ptr p1, plural_ptr p2) : binary(p1, p2) {} \ + \ + int operator()(int n) const override { return (*op1)(n)oper(*op2)(n); } \ + name* clone() const override \ + { \ + plural_ptr op1_copy(op1->clone()); \ + plural_ptr op2_copy(op2->clone()); \ + return new name(op1_copy, op2_copy); \ + } \ + }; + +#define BINOPD(name, oper) \ + struct name : public binary { \ + name(plural_ptr p1, plural_ptr p2) : binary(p1, p2) {} \ + int operator()(int n) const override \ + { \ + int v1 = (*op1)(n); \ + int v2 = (*op2)(n); \ + return v2 == 0 ? 0 : v1 oper v2; \ + } \ + name* clone() const override \ + { \ + plural_ptr op1_copy(op1->clone()); \ + plural_ptr op2_copy(op2->clone()); \ + return new name(op1_copy, op2_copy); \ + } \ + }; + + enum { END = 0, SHL = 256, SHR, GTE, LTE, EQ, NEQ, AND, OR, NUM, VARIABLE }; + + UNOP(l_not, !) + UNOP(minus, -) + UNOP(bin_not, ~) + + BINOP(mul, *) + BINOPD(div, /) + BINOPD(mod, %) + static int level10[] = {3, '*', '/', '%'}; + + BINOP(add, +) + BINOP(sub, -) + static int level9[] = {2, '+', '-'}; + + BINOP(shl, <<) + BINOP(shr, >>) + static int level8[] = {2, SHL, SHR}; + + BINOP(gt, >) + BINOP(lt, <) + BINOP(gte, >=) + BINOP(lte, <=) + static int level7[] = {4, '<', '>', GTE, LTE}; + + BINOP(eq, ==) + BINOP(neq, !=) + static int level6[] = {2, EQ, NEQ}; + + BINOP(bin_and, &) + static int level5[] = {1, '&'}; + + BINOP(bin_xor, ^) + static int level4[] = {1, '^'}; + + BINOP(bin_or, |) + static int level3[] = {1, '|'}; + + BINOP(l_and, &&) + static int level2[] = {1, AND}; + + BINOP(l_or, ||) + static int level1[] = {1, OR}; + + struct conditional : public plural { + conditional(plural_ptr p1, plural_ptr p2, plural_ptr p3) : op1(p1), op2(p2), op3(p3) {} + int operator()(int n) const override { return (*op1)(n) ? (*op2)(n) : (*op3)(n); } + conditional* clone() const override + { + plural_ptr op1_copy(op1->clone()); + plural_ptr op2_copy(op2->clone()); + plural_ptr op3_copy(op3->clone()); + return new conditional(op1_copy, op2_copy, op3_copy); + } + + private: + plural_ptr op1, op2, op3; + }; + + plural_ptr bin_factory(int value, plural_ptr left, plural_ptr right) + { + switch(value) { + case '/': return plural_ptr(new div(left, right)); + case '*': return plural_ptr(new mul(left, right)); + case '%': return plural_ptr(new mod(left, right)); + case '+': return plural_ptr(new add(left, right)); + case '-': return plural_ptr(new sub(left, right)); + case SHL: return plural_ptr(new shl(left, right)); + case SHR: return plural_ptr(new shr(left, right)); + case '>': return plural_ptr(new gt(left, right)); + case '<': return plural_ptr(new lt(left, right)); + case GTE: return plural_ptr(new gte(left, right)); + case LTE: return plural_ptr(new lte(left, right)); + case EQ: return plural_ptr(new eq(left, right)); + case NEQ: return plural_ptr(new neq(left, right)); + case '&': return plural_ptr(new bin_and(left, right)); + case '^': return plural_ptr(new bin_xor(left, right)); + case '|': return plural_ptr(new bin_or(left, right)); + case AND: return plural_ptr(new l_and(left, right)); + case OR: return plural_ptr(new l_or(left, right)); + default: return plural_ptr(); + } + } + + static inline bool is_in(int v, int* p) + { + int len = *p; + p++; + while(len && *p != v) { + p++; + len--; + } + return len != 0; + } + + class tokenizer { + public: + tokenizer(const char* s) + { + text = s; + pos = 0; + step(); + }; + int get(int* val = NULL) + { + int iv = int_value; + int res = next_tocken; + step(); + if(val && res == NUM) { + *val = iv; + } + return res; + }; + int next(int* val = NULL) + { + if(val && next_tocken == NUM) { + *val = int_value; + return NUM; + } + return next_tocken; + } + + private: + const char* text; + size_t pos; + int next_tocken; + int int_value; + bool is_blank(char c) { return c == ' ' || c == '\r' || c == '\n' || c == '\t'; } + bool isdigit(char c) { return '0' <= c && c <= '9'; } + void step() + { + while(text[pos] && is_blank(text[pos])) + pos++; + const char* ptr = text + pos; + char* tmp_ptr; + if(strncmp(ptr, "<<", 2) == 0) { + pos += 2; + next_tocken = SHL; + } else if(strncmp(ptr, ">>", 2) == 0) { + pos += 2; + next_tocken = SHR; + } else if(strncmp(ptr, "&&", 2) == 0) { + pos += 2; + next_tocken = AND; + } else if(strncmp(ptr, "||", 2) == 0) { + pos += 2; + next_tocken = OR; + } else if(strncmp(ptr, "<=", 2) == 0) { + pos += 2; + next_tocken = LTE; + } else if(strncmp(ptr, ">=", 2) == 0) { + pos += 2; + next_tocken = GTE; + } else if(strncmp(ptr, "==", 2) == 0) { + pos += 2; + next_tocken = EQ; + } else if(strncmp(ptr, "!=", 2) == 0) { + pos += 2; + next_tocken = NEQ; + } else if(*ptr == 'n') { + pos++; + next_tocken = VARIABLE; + } else if(isdigit(*ptr)) { + int_value = strtol(text + pos, &tmp_ptr, 0); + pos = tmp_ptr - text; + next_tocken = NUM; + } else if(*ptr == '\0') { + next_tocken = 0; + } else { + next_tocken = *ptr; + pos++; + } + } + }; + +#define BINARY_EXPR(expr, hexpr, list) \ + plural_ptr expr() \ + { \ + plural_ptr op1, op2; \ + if((op1 = hexpr()).get() == 0) \ + return plural_ptr(); \ + while(is_in(t.next(), list)) { \ + int o = t.get(); \ + if((op2 = hexpr()).get() == 0) \ + return plural_ptr(); \ + op1 = bin_factory(o, op1, op2); \ + } \ + return op1; \ + } + + class parser { + public: + parser(tokenizer& tin) : t(tin){}; + + plural_ptr compile() + { + plural_ptr res = cond_expr(); + if(res.get() && t.next() != END) { + return plural_ptr(); + }; + return res; + } + + private: + plural_ptr value_expr() + { + plural_ptr op; + if(t.next() == '(') { + t.get(); + if((op = cond_expr()).get() == 0) + return plural_ptr(); + if(t.get() != ')') + return plural_ptr(); + return op; + } else if(t.next() == NUM) { + int value; + t.get(&value); + return plural_ptr(new number(value)); + } else if(t.next() == VARIABLE) { + t.get(); + return plural_ptr(new identity()); + } + return plural_ptr(); + }; + + plural_ptr un_expr() + { + plural_ptr op1; + static int level_unary[] = {3, '-', '!', '~'}; + if(is_in(t.next(), level_unary)) { + int op = t.get(); + if((op1 = un_expr()).get() == 0) + return plural_ptr(); + switch(op) { + case '-': return plural_ptr(new minus(op1)); + case '!': return plural_ptr(new l_not(op1)); + case '~': return plural_ptr(new bin_not(op1)); + default: return plural_ptr(); + } + } else { + return value_expr(); + } + }; + + BINARY_EXPR(l10, un_expr, level10); + BINARY_EXPR(l9, l10, level9); + BINARY_EXPR(l8, l9, level8); + BINARY_EXPR(l7, l8, level7); + BINARY_EXPR(l6, l7, level6); + BINARY_EXPR(l5, l6, level5); + BINARY_EXPR(l4, l5, level4); + BINARY_EXPR(l3, l4, level3); + BINARY_EXPR(l2, l3, level2); + BINARY_EXPR(l1, l2, level1); + + plural_ptr cond_expr() + { + plural_ptr cond, case1, case2; + if((cond = l1()).get() == 0) + return plural_ptr(); + if(t.next() == '?') { + t.get(); + if((case1 = cond_expr()).get() == 0) + return plural_ptr(); + if(t.get() != ':') + return plural_ptr(); + if((case2 = cond_expr()).get() == 0) + return plural_ptr(); + } else { + return cond; + } + return plural_ptr(new conditional(cond, case1, case2)); + } + + tokenizer& t; + }; + + } // namespace + + plural_ptr compile(const char* str) + { + tokenizer t(str); + parser p(t); + return p.compile(); + } + +}}}} // namespace boost::locale::gnu_gettext::lambda diff --git a/libs/locale/src/boost/locale/shared/mo_lambda.hpp b/libs/locale/src/boost/locale/shared/mo_lambda.hpp new file mode 100644 index 0000000..71ae35a --- /dev/null +++ b/libs/locale/src/boost/locale/shared/mo_lambda.hpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_SRC_LOCALE_MO_LAMBDA_HPP_INCLUDED +#define BOOST_SRC_LOCALE_MO_LAMBDA_HPP_INCLUDED + +#include +#include + +namespace boost { namespace locale { namespace gnu_gettext { namespace lambda { + + struct plural { + virtual int operator()(int n) const = 0; + virtual plural* clone() const = 0; + virtual ~plural() = default; + }; + + typedef std::shared_ptr plural_ptr; + + plural_ptr compile(const char* c_expression); + +}}}} // namespace boost::locale::gnu_gettext::lambda + +#endif diff --git a/libs/locale/src/boost/locale/std/all_generator.hpp b/libs/locale/src/boost/locale/std/all_generator.hpp new file mode 100644 index 0000000..81e3102 --- /dev/null +++ b/libs/locale/src/boost/locale/std/all_generator.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_STD_ALL_GENERATOR_HPP +#define BOOST_LOCALE_IMPL_STD_ALL_GENERATOR_HPP + +#include +#include +#include + +namespace boost { namespace locale { namespace impl_std { + enum class utf8_support { none, native, native_with_wide, from_wide }; + + std::locale create_convert(const std::locale& in, + const std::string& locale_name, + char_facet_t type, + utf8_support utf = utf8_support::none); + + std::locale create_collate(const std::locale& in, + const std::string& locale_name, + char_facet_t type, + utf8_support utf = utf8_support::none); + + std::locale create_formatting(const std::locale& in, + const std::string& locale_name, + char_facet_t type, + utf8_support utf = utf8_support::none); + + std::locale create_parsing(const std::locale& in, + const std::string& locale_name, + char_facet_t type, + utf8_support utf = utf8_support::none); + + std::locale create_codecvt(const std::locale& in, + const std::string& locale_name, + char_facet_t type, + utf8_support utf = utf8_support::none); + +}}} // namespace boost::locale::impl_std + +#endif diff --git a/libs/locale/src/boost/locale/std/codecvt.cpp b/libs/locale/src/boost/locale/std/codecvt.cpp new file mode 100644 index 0000000..e5f702a --- /dev/null +++ b/libs/locale/src/boost/locale/std/codecvt.cpp @@ -0,0 +1,38 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include "boost/locale/std/all_generator.hpp" +#include + +namespace boost { namespace locale { namespace impl_std { + template + std::locale codecvt_bychar(const std::locale& in, const std::string& locale_name) + { + return std::locale(in, new std::codecvt_byname(locale_name.c_str())); + } + + std::locale + create_codecvt(const std::locale& in, const std::string& locale_name, char_facet_t type, utf8_support utf) + { + if(utf == utf8_support::from_wide) + return util::create_utf8_codecvt(in, type); + + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return codecvt_bychar(in, locale_name); + case char_facet_t::wchar_f: return codecvt_bychar(in, locale_name); +#if defined(BOOST_LOCALE_ENABLE_CHAR16_T) + case char_facet_t::char16_f: return codecvt_bychar(in, locale_name); +#endif +#if defined(BOOST_LOCALE_ENABLE_CHAR32_T) + case char_facet_t::char32_f: return codecvt_bychar(in, locale_name); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_std diff --git a/libs/locale/src/boost/locale/std/collate.cpp b/libs/locale/src/boost/locale/std/collate.cpp new file mode 100644 index 0000000..ced58ec --- /dev/null +++ b/libs/locale/src/boost/locale/std/collate.cpp @@ -0,0 +1,92 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include "boost/locale/std/all_generator.hpp" +#include +#include +#include + +namespace boost { namespace locale { namespace impl_std { + + class utf8_collator_from_wide : public std::collate { + public: + typedef std::collate wfacet; + utf8_collator_from_wide(const std::locale& base, size_t refs = 0) : std::collate(refs), base_(base) {} + int do_compare(const char* lb, const char* le, const char* rb, const char* re) const override + { + std::wstring l = conv::to_utf(lb, le, "UTF-8"); + std::wstring r = conv::to_utf(rb, re, "UTF-8"); + return std::use_facet(base_).compare(l.c_str(), + l.c_str() + l.size(), + r.c_str(), + r.c_str() + r.size()); + } + long do_hash(const char* b, const char* e) const override + { + std::wstring tmp = conv::to_utf(b, e, "UTF-8"); + return std::use_facet(base_).hash(tmp.c_str(), tmp.c_str() + tmp.size()); + } + std::string do_transform(const char* b, const char* e) const override + { + std::wstring tmp = conv::to_utf(b, e, "UTF-8"); + std::wstring wkey = std::use_facet(base_).transform(tmp.c_str(), tmp.c_str() + tmp.size()); + std::string key; + BOOST_LOCALE_START_CONST_CONDITION + if(sizeof(wchar_t) == 2) + key.reserve(wkey.size() * 2); + else + key.reserve(wkey.size() * 3); + for(unsigned i = 0; i < wkey.size(); i++) { + if(sizeof(wchar_t) == 2) { + uint16_t tv = static_cast(wkey[i]); + key += char(tv >> 8); + key += char(tv & 0xFF); + } else { // 4 + uint32_t tv = static_cast(wkey[i]); + // 21 bit + key += char((tv >> 16) & 0xFF); + key += char((tv >> 8) & 0xFF); + key += char(tv & 0xFF); + } + } + BOOST_LOCALE_END_CONST_CONDITION + return key; + } + + private: + std::locale base_; + }; + + std::locale + create_collate(const std::locale& in, const std::string& locale_name, char_facet_t type, utf8_support utf) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: { + if(utf == utf8_support::from_wide) { + std::locale base = + std::locale(std::locale::classic(), new std::collate_byname(locale_name.c_str())); + return std::locale(in, new utf8_collator_from_wide(base)); + } else { + return std::locale(in, new std::collate_byname(locale_name.c_str())); + } + } + + case char_facet_t::wchar_f: return std::locale(in, new std::collate_byname(locale_name.c_str())); + +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new std::collate_byname(locale_name.c_str())); +#endif + +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new std::collate_byname(locale_name.c_str())); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_std diff --git a/libs/locale/src/boost/locale/std/converter.cpp b/libs/locale/src/boost/locale/std/converter.cpp new file mode 100644 index 0000000..a883ef7 --- /dev/null +++ b/libs/locale/src/boost/locale/std/converter.cpp @@ -0,0 +1,124 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +#include "boost/locale/std/all_generator.hpp" + +namespace boost { namespace locale { namespace impl_std { + + template + class std_converter : public converter { + public: + typedef CharType char_type; + typedef std::basic_string string_type; + typedef std::ctype ctype_type; + std_converter(const std::locale& base, size_t refs = 0) : converter(refs), base_(base) {} + string_type convert(converter_base::conversion_type how, + const char_type* begin, + const char_type* end, + int /*flags*/ = 0) const override + { + switch(how) { + case converter_base::upper_case: + case converter_base::lower_case: + case converter_base::case_folding: { + const ctype_type& ct = std::use_facet(base_); + size_t len = end - begin; + std::vector res(len + 1, 0); + char_type* lbegin = &res[0]; + std::copy(begin, end, lbegin); + if(how == converter_base::upper_case) + ct.toupper(lbegin, lbegin + len); + else + ct.tolower(lbegin, lbegin + len); + return string_type(lbegin, len); + } + case converter_base::normalization: + case converter_base::title_case: break; + } + return string_type(begin, end - begin); + } + + private: + std::locale base_; + }; + + class utf8_converter : public converter { + public: + typedef std::ctype ctype_type; + typedef std::ctype wctype_type; + utf8_converter(const std::locale& base, size_t refs = 0) : converter(refs), base_(base) {} + std::string convert(converter_base::conversion_type how, + const char* begin, + const char* end, + int /*flags*/ = 0) const override + { + switch(how) { + case upper_case: + case lower_case: + case case_folding: { + std::wstring tmp = conv::to_utf(begin, end, "UTF-8"); + const wctype_type& ct = std::use_facet(base_); + size_t len = tmp.size(); + std::vector res(len + 1, 0); + wchar_t* lbegin = &res[0]; + std::copy(tmp.c_str(), tmp.c_str() + len, lbegin); + if(how == upper_case) + ct.toupper(lbegin, lbegin + len); + else + ct.tolower(lbegin, lbegin + len); + return conv::from_utf(lbegin, lbegin + len, "UTF-8"); + } + case title_case: + case normalization: break; + } + return std::string(begin, end - begin); + } + + private: + std::locale base_; + }; + + std::locale + create_convert(const std::locale& in, const std::string& locale_name, char_facet_t type, utf8_support utf) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: { + if(utf == utf8_support::native_with_wide || utf == utf8_support::from_wide) { + std::locale base(std::locale::classic(), new std::ctype_byname(locale_name.c_str())); + return std::locale(in, new utf8_converter(base)); + } + std::locale base(std::locale::classic(), new std::ctype_byname(locale_name.c_str())); + return std::locale(in, new std_converter(base)); + } + case char_facet_t::wchar_f: { + std::locale base(std::locale::classic(), new std::ctype_byname(locale_name.c_str())); + return std::locale(in, new std_converter(base)); + } +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: { + std::locale base(std::locale::classic(), new std::ctype_byname(locale_name.c_str())); + return std::locale(in, new std_converter(base)); + } +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: { + std::locale base(std::locale::classic(), new std::ctype_byname(locale_name.c_str())); + return std::locale(in, new std_converter(base)); + } +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_std diff --git a/libs/locale/src/boost/locale/std/numeric.cpp b/libs/locale/src/boost/locale/std/numeric.cpp new file mode 100644 index 0000000..2f93d2c --- /dev/null +++ b/libs/locale/src/boost/locale/std/numeric.cpp @@ -0,0 +1,377 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "boost/locale/std/all_generator.hpp" +#include "boost/locale/util/numeric.hpp" + +namespace boost { namespace locale { namespace impl_std { + + template + class time_put_from_base : public std::time_put { + public: + time_put_from_base(const std::locale& base, size_t refs = 0) : std::time_put(refs), base_(base) {} + typedef typename std::time_put::iter_type iter_type; + + iter_type do_put(iter_type out, + std::ios_base& /*ios*/, + CharType fill, + const std::tm* tm, + char format, + char modifier) const override + { + std::basic_stringstream ss; + ss.imbue(base_); + return std::use_facet>(base_).put(out, ss, fill, tm, format, modifier); + } + + private: + std::locale base_; + }; + + class utf8_time_put_from_wide : public std::time_put { + public: + utf8_time_put_from_wide(const std::locale& base, size_t refs = 0) : std::time_put(refs), base_(base) {} + iter_type do_put(iter_type out, + std::ios_base& /*ios*/, + char fill, + const std::tm* tm, + char format, + char modifier = 0) const override + { + std::basic_ostringstream wtmps; + wtmps.imbue(base_); + std::use_facet>(base_) + .put(wtmps, wtmps, wchar_t(fill), tm, wchar_t(format), wchar_t(modifier)); + std::wstring wtmp = wtmps.str(); + std::string const tmp = conv::from_utf(wtmp, "UTF-8"); + for(unsigned i = 0; i < tmp.size(); i++) { + *out++ = tmp[i]; + } + return out; + } + + private: + std::locale base_; + }; + + class utf8_numpunct_from_wide : public std::numpunct { + public: + utf8_numpunct_from_wide(const std::locale& base, size_t refs = 0) : std::numpunct(refs) + { + typedef std::numpunct wfacet_type; + const wfacet_type& wfacet = std::use_facet(base); + + truename_ = conv::from_utf(wfacet.truename(), "UTF-8"); + falsename_ = conv::from_utf(wfacet.falsename(), "UTF-8"); + + wchar_t tmp_decimal_point = wfacet.decimal_point(); + wchar_t tmp_thousands_sep = wfacet.thousands_sep(); + std::string tmp_grouping = wfacet.grouping(); + + if(32 <= tmp_thousands_sep && tmp_thousands_sep <= 126 && 32 <= tmp_decimal_point + && tmp_decimal_point <= 126) { + thousands_sep_ = static_cast(tmp_thousands_sep); + decimal_point_ = static_cast(tmp_decimal_point); + grouping_ = tmp_grouping; + } else if(32 <= tmp_decimal_point && tmp_decimal_point <= 126 && tmp_thousands_sep == 0xA0) { + // workaround common bug - substitute NBSP with ordinary space + thousands_sep_ = ' '; + decimal_point_ = static_cast(tmp_decimal_point); + grouping_ = tmp_grouping; + } else if(32 <= tmp_decimal_point && tmp_decimal_point <= 126) { + thousands_sep_ = ','; + decimal_point_ = static_cast(tmp_decimal_point); + grouping_ = std::string(); + } else { + thousands_sep_ = ','; + decimal_point_ = '.'; + grouping_ = std::string(); + } + } + + char do_decimal_point() const override { return decimal_point_; } + char do_thousands_sep() const override { return thousands_sep_; } + std::string do_grouping() const override { return grouping_; } + std::string do_truename() const override { return truename_; } + std::string do_falsename() const override { return falsename_; } + + private: + std::string truename_; + std::string falsename_; + char thousands_sep_; + char decimal_point_; + std::string grouping_; + }; + + template + class utf8_moneypunct_from_wide : public std::moneypunct { + public: + utf8_moneypunct_from_wide(const std::locale& base, size_t refs = 0) : std::moneypunct(refs) + { + typedef std::moneypunct wfacet_type; + const wfacet_type& wfacet = std::use_facet(base); + + curr_symbol_ = conv::from_utf(wfacet.curr_symbol(), "UTF-8"); + positive_sign_ = conv::from_utf(wfacet.positive_sign(), "UTF-8"); + negative_sign_ = conv::from_utf(wfacet.negative_sign(), "UTF-8"); + frac_digits_ = wfacet.frac_digits(); + pos_format_ = wfacet.pos_format(); + neg_format_ = wfacet.neg_format(); + + wchar_t tmp_decimal_point = wfacet.decimal_point(); + wchar_t tmp_thousands_sep = wfacet.thousands_sep(); + std::string tmp_grouping = wfacet.grouping(); + if(32 <= tmp_thousands_sep && tmp_thousands_sep <= 126 && 32 <= tmp_decimal_point + && tmp_decimal_point <= 126) { + thousands_sep_ = static_cast(tmp_thousands_sep); + decimal_point_ = static_cast(tmp_decimal_point); + grouping_ = tmp_grouping; + } else if(32 <= tmp_decimal_point && tmp_decimal_point <= 126 && tmp_thousands_sep == 0xA0) { + // workaround common bug - substitute NBSP with ordinary space + thousands_sep_ = ' '; + decimal_point_ = static_cast(tmp_decimal_point); + grouping_ = tmp_grouping; + } else if(32 <= tmp_decimal_point && tmp_decimal_point <= 126) { + thousands_sep_ = ','; + decimal_point_ = static_cast(tmp_decimal_point); + grouping_ = std::string(); + } else { + thousands_sep_ = ','; + decimal_point_ = '.'; + grouping_ = std::string(); + } + } + + char do_decimal_point() const override { return decimal_point_; } + + char do_thousands_sep() const override { return thousands_sep_; } + + std::string do_grouping() const override { return grouping_; } + + std::string do_curr_symbol() const override { return curr_symbol_; } + std::string do_positive_sign() const override { return positive_sign_; } + std::string do_negative_sign() const override { return negative_sign_; } + + int do_frac_digits() const override { return frac_digits_; } + + std::money_base::pattern do_pos_format() const override { return pos_format_; } + + std::money_base::pattern do_neg_format() const override { return neg_format_; } + + private: + char thousands_sep_; + char decimal_point_; + std::string grouping_; + std::string curr_symbol_; + std::string positive_sign_; + std::string negative_sign_; + int frac_digits_; + std::money_base::pattern pos_format_, neg_format_; + }; + + class utf8_numpunct : public std::numpunct_byname { + public: + typedef std::numpunct_byname base_type; + utf8_numpunct(const char* name, size_t refs = 0) : std::numpunct_byname(name, refs) {} + char do_thousands_sep() const override + { + unsigned char bs = base_type::do_thousands_sep(); + if(bs > 127) + if(bs == 0xA0) + return ' '; + else + return 0; + else + return bs; + } + std::string do_grouping() const override + { + unsigned char bs = base_type::do_thousands_sep(); + if(bs > 127 && bs != 0xA0) + return std::string(); + return base_type::do_grouping(); + } + }; + + template + class utf8_moneypunct : public std::moneypunct_byname { + public: + typedef std::moneypunct_byname base_type; + utf8_moneypunct(const char* name, size_t refs = 0) : std::moneypunct_byname(name, refs) {} + char do_thousands_sep() const override + { + unsigned char bs = base_type::do_thousands_sep(); + if(bs > 127) + if(bs == 0xA0) + return ' '; + else + return 0; + else + return bs; + } + std::string do_grouping() const override + { + unsigned char bs = base_type::do_thousands_sep(); + if(bs > 127 && bs != 0xA0) + return std::string(); + return base_type::do_grouping(); + } + }; + + template + std::locale create_basic_parsing(const std::locale& in, const std::string& locale_name) + { + std::locale tmp = std::locale(in, new std::numpunct_byname(locale_name.c_str())); + tmp = std::locale(tmp, new std::moneypunct_byname(locale_name.c_str())); + tmp = std::locale(tmp, new std::moneypunct_byname(locale_name.c_str())); + tmp = std::locale(tmp, new std::ctype_byname(locale_name.c_str())); + return tmp; + } + + template + std::locale create_basic_formatting(const std::locale& in, const std::string& locale_name) + { + std::locale tmp = create_basic_parsing(in, locale_name); + std::locale base(locale_name.c_str()); + tmp = std::locale(tmp, new time_put_from_base(base)); + return tmp; + } + + std::locale + create_formatting(const std::locale& in, const std::string& locale_name, char_facet_t type, utf8_support utf) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: { + switch(utf) { + case utf8_support::from_wide: { + std::locale base = std::locale(locale_name.c_str()); + + std::locale tmp = std::locale(in, new utf8_time_put_from_wide(base)); + tmp = std::locale(tmp, new utf8_numpunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + return std::locale(tmp, new util::base_num_format()); + } + case utf8_support::native: { + std::locale base = std::locale(locale_name.c_str()); + + std::locale tmp = std::locale(in, new time_put_from_base(base)); + tmp = std::locale(tmp, new utf8_numpunct(locale_name.c_str())); + tmp = std::locale(tmp, new utf8_moneypunct(locale_name.c_str())); + tmp = std::locale(tmp, new utf8_moneypunct(locale_name.c_str())); + return std::locale(tmp, new util::base_num_format()); + } + case utf8_support::native_with_wide: { + std::locale base = std::locale(locale_name.c_str()); + + std::locale tmp = std::locale(in, new time_put_from_base(base)); + tmp = std::locale(tmp, new utf8_numpunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + return std::locale(tmp, new util::base_num_format()); + } + case utf8_support::none: break; + } + std::locale tmp = create_basic_formatting(in, locale_name); + tmp = std::locale(tmp, new util::base_num_format()); + return tmp; + } + case char_facet_t::wchar_f: { + std::locale tmp = create_basic_formatting(in, locale_name); + tmp = std::locale(tmp, new util::base_num_format()); + return tmp; + } +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: { + std::locale tmp = create_basic_formatting(in, locale_name); + tmp = std::locale(tmp, new util::base_num_format()); + return tmp; + } +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: { + std::locale tmp = create_basic_formatting(in, locale_name); + tmp = std::locale(tmp, new util::base_num_format()); + return tmp; + } +#endif + } + return in; + } + + std::locale + create_parsing(const std::locale& in, const std::string& locale_name, char_facet_t type, utf8_support utf) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: { + switch(utf) { + case utf8_support::from_wide: { + std::locale base = std::locale::classic(); + + base = std::locale(base, new std::numpunct_byname(locale_name.c_str())); + base = std::locale(base, new std::moneypunct_byname(locale_name.c_str())); + base = std::locale(base, new std::moneypunct_byname(locale_name.c_str())); + + std::locale tmp = std::locale(in, new utf8_numpunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + return std::locale(tmp, new util::base_num_parse()); + } + case utf8_support::native: { + std::locale tmp = std::locale(in, new utf8_numpunct(locale_name.c_str())); + tmp = std::locale(tmp, new utf8_moneypunct(locale_name.c_str())); + tmp = std::locale(tmp, new utf8_moneypunct(locale_name.c_str())); + return std::locale(tmp, new util::base_num_parse()); + } + case utf8_support::native_with_wide: { + std::locale base = std::locale(locale_name.c_str()); + + std::locale tmp = std::locale(in, new utf8_numpunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + tmp = std::locale(tmp, new utf8_moneypunct_from_wide(base)); + return std::locale(tmp, new util::base_num_parse()); + } + case utf8_support::none: break; + } + std::locale tmp = create_basic_parsing(in, locale_name); + tmp = std::locale(in, new util::base_num_parse()); + return tmp; + } + case char_facet_t::wchar_f: { + std::locale tmp = create_basic_parsing(in, locale_name); + tmp = std::locale(in, new util::base_num_parse()); + return tmp; + } +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: { + std::locale tmp = create_basic_parsing(in, locale_name); + tmp = std::locale(in, new util::base_num_parse()); + return tmp; + } +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: { + std::locale tmp = create_basic_parsing(in, locale_name); + tmp = std::locale(in, new util::base_num_parse()); + return tmp; + } +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_std diff --git a/libs/locale/src/boost/locale/std/std_backend.cpp b/libs/locale/src/boost/locale/std/std_backend.cpp new file mode 100644 index 0000000..05967eb --- /dev/null +++ b/libs/locale/src/boost/locale/std/std_backend.cpp @@ -0,0 +1,208 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/std/std_backend.hpp" +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_WINDOWS) +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include "boost/locale/encoding/conv.hpp" +# include "boost/locale/util/encoding.hpp" +# include "boost/locale/win32/lcid.hpp" +# include +#endif +#include "boost/locale/std/all_generator.hpp" +#include "boost/locale/util/gregorian.hpp" + +namespace boost { namespace locale { namespace impl_std { + + class std_localization_backend : public localization_backend { + public: + std_localization_backend() : invalid_(true), use_ansi_encoding_(false) {} + std_localization_backend(const std_localization_backend& other) : + localization_backend(), paths_(other.paths_), domains_(other.domains_), locale_id_(other.locale_id_), + invalid_(true), use_ansi_encoding_(other.use_ansi_encoding_) + {} + std_localization_backend* clone() const override { return new std_localization_backend(*this); } + + void set_option(const std::string& name, const std::string& value) override + { + invalid_ = true; + if(name == "locale") + locale_id_ = value; + else if(name == "message_path") + paths_.push_back(value); + else if(name == "message_application") + domains_.push_back(value); + else if(name == "use_ansi_encoding") + use_ansi_encoding_ = value == "true"; + } + void clear_options() override + { + invalid_ = true; + use_ansi_encoding_ = false; + locale_id_.clear(); + paths_.clear(); + domains_.clear(); + } + + void prepare_data() + { + if(!invalid_) + return; + invalid_ = false; + std::string lid = locale_id_; + if(lid.empty()) { + bool use_utf8 = !use_ansi_encoding_; + lid = util::get_system_locale(use_utf8); + } + in_use_id_ = lid; + data_.parse(lid); + name_ = "C"; + +#if defined(BOOST_WINDOWS) + const std::pair wl_inf = to_windows_name(lid); + const std::string& win_name = wl_inf.first; + const int win_codepage = wl_inf.second; +#endif + + if(!data_.is_utf8()) { + if(loadable(lid)) + name_ = lid; +#if defined(BOOST_WINDOWS) + else if(loadable(win_name) && win_codepage == util::encoding_to_windows_codepage(data_.encoding())) + name_ = win_name; +#endif + utf_mode_ = utf8_support::none; + } else { + if(loadable(lid)) { + name_ = lid; + utf_mode_ = utf8_support::native_with_wide; +#if defined(BOOST_WINDOWS) + // This isn't fully correct: + // It will treat the 2-Byte wchar_t as UTF-16 encoded while it may be UCS-2 + // std::basic_filebuf explicitely disallows using suche multi-byte codecvts + // but it works in practice so far, so use it instead of failing for codepoints above U+FFFF + utf_mode_ = utf8_support::from_wide; +#endif + } +#if defined(BOOST_WINDOWS) + else if(loadable(win_name)) + { + name_ = win_name; + utf_mode_ = utf8_support::from_wide; + } +#endif + else + utf_mode_ = utf8_support::none; + } + } + +#if defined(BOOST_WINDOWS) + std::pair to_windows_name(const std::string& l) + { + std::pair res("C", 0); + unsigned lcid = impl_win::locale_to_lcid(l); + char win_lang[256] = {0}; + char win_country[256] = {0}; + char win_codepage[10] = {0}; + if(GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE, win_lang, sizeof(win_lang)) == 0) + return res; + std::string lc_name = win_lang; + if(GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY, win_country, sizeof(win_country)) != 0) { + lc_name += "_"; + lc_name += win_country; + } + + res.first = lc_name; + + if(GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, win_codepage, sizeof(win_codepage)) != 0) + res.second = atoi(win_codepage); + return res; + } +#endif + + bool loadable(std::string name) + { + try { + std::locale l(name.c_str()); + return true; + } catch(const std::exception& /*e*/) { + return false; + } + } + + std::locale install(const std::locale& base, category_t category, char_facet_t type) override + { + prepare_data(); + + switch(category) { + case category_t::convert: return create_convert(base, name_, type, utf_mode_); + case category_t::collation: return create_collate(base, name_, type, utf_mode_); + case category_t::formatting: return create_formatting(base, name_, type, utf_mode_); + case category_t::parsing: return create_parsing(base, name_, type, utf_mode_); + case category_t::codepage: return create_codecvt(base, name_, type, utf_mode_); + case category_t::calendar: return util::install_gregorian_calendar(base, data_.country()); + case category_t::message: { + gnu_gettext::messages_info minf; + minf.language = data_.language(); + minf.country = data_.country(); + minf.variant = data_.variant(); + minf.encoding = data_.encoding(); + std::copy(domains_.begin(), + domains_.end(), + std::back_inserter(minf.domains)); + minf.paths = paths_; + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); + case char_facet_t::wchar_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif + } + return base; + } + case category_t::information: return util::create_info(base, in_use_id_); + case category_t::boundary: break; // Not implemented + } + return base; + } + + private: + std::vector paths_; + std::vector domains_; + std::string locale_id_; + + util::locale_data data_; + std::string name_; + std::string in_use_id_; + utf8_support utf_mode_; + bool invalid_; + bool use_ansi_encoding_; + }; + + localization_backend* create_localization_backend() + { + return new std_localization_backend(); + } + +}}} // namespace boost::locale::impl_std diff --git a/libs/locale/src/boost/locale/std/std_backend.hpp b/libs/locale/src/boost/locale/std/std_backend.hpp new file mode 100644 index 0000000..76ddc05 --- /dev/null +++ b/libs/locale/src/boost/locale/std/std_backend.hpp @@ -0,0 +1,15 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_STD_LOCALIZATION_BACKEND_HPP +#define BOOST_LOCALE_IMPL_STD_LOCALIZATION_BACKEND_HPP +namespace boost { namespace locale { + class localization_backend; + namespace impl_std { + localization_backend* create_localization_backend(); + } // namespace impl_std +}} // namespace boost::locale +#endif diff --git a/libs/locale/src/boost/locale/util/codecvt_converter.cpp b/libs/locale/src/boost/locale/util/codecvt_converter.cpp new file mode 100644 index 0000000..4075988 --- /dev/null +++ b/libs/locale/src/boost/locale/util/codecvt_converter.cpp @@ -0,0 +1,325 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include + +#include "boost/locale/encoding/conv.hpp" +#include "boost/locale/util/encoding.hpp" + +#ifdef BOOST_MSVC +# pragma warning(disable : 4244) // loose data +#endif + +namespace boost { namespace locale { namespace util { + + base_converter::~base_converter() = default; + + class utf8_converter : public base_converter { + public: + int max_len() const override { return 4; } + + utf8_converter* clone() const override { return new utf8_converter(); } + + bool is_thread_safe() const override { return true; } + + uint32_t to_unicode(const char*& begin, const char* end) override + { + const char* p = begin; + + utf::code_point c = utf::utf_traits::decode(p, end); + + if(c == utf::illegal) + return illegal; + + if(c == utf::incomplete) + return incomplete; + + begin = p; + return c; + } + + uint32_t from_unicode(uint32_t u, char* begin, const char* end) override + { + if(!utf::is_valid_codepoint(u)) + return illegal; + int width = utf::utf_traits::width(u); + std::ptrdiff_t d = end - begin; + if(d < width) + return incomplete; + utf::utf_traits::encode(u, begin); + return width; + } + }; // utf8_converter + + class simple_converter_impl { + public: + static constexpr int hash_table_size = 1024; + + simple_converter_impl(const std::string& encoding) + { + for(unsigned i = 0; i < 128; i++) + to_unicode_tbl_[i] = i; + for(unsigned i = 128; i < 256; i++) { + char buf[2] = {char(i), 0}; + uint32_t uchar = utf::illegal; + try { + std::wstring const tmp = conv::to_utf(buf, buf + 1, encoding, conv::stop); + if(tmp.size() == 1) { + uchar = tmp[0]; + } else { + uchar = utf::illegal; + } + } catch(const conv::conversion_error& /*e*/) { + uchar = utf::illegal; + } + to_unicode_tbl_[i] = uchar; + } + for(int i = 0; i < hash_table_size; i++) + from_unicode_tbl_[i] = 0; + for(unsigned i = 1; i < 256; i++) { + if(to_unicode_tbl_[i] != utf::illegal) { + unsigned pos = to_unicode_tbl_[i] % hash_table_size; + while(from_unicode_tbl_[pos] != 0) + pos = (pos + 1) % hash_table_size; + from_unicode_tbl_[pos] = i; + } + } + } + + uint32_t to_unicode(const char*& begin, const char* end) const + { + if(begin == end) + return utf::incomplete; + unsigned char c = *begin++; + return to_unicode_tbl_[c]; + } + uint32_t from_unicode(uint32_t u, char* begin, const char* end) const + { + if(begin == end) + return utf::incomplete; + if(u == 0) { + *begin = 0; + return 1; + } + unsigned pos = u % hash_table_size; + unsigned char c; + while((c = from_unicode_tbl_[pos]) != 0 && to_unicode_tbl_[c] != u) + pos = (pos + 1) % hash_table_size; + if(c == 0) + return utf::illegal; + *begin = c; + return 1; + } + + private: + uint32_t to_unicode_tbl_[256]; + unsigned char from_unicode_tbl_[hash_table_size]; + }; + + class simple_converter : public base_converter { + public: + simple_converter(const std::string& encoding) : cvt_(encoding) {} + + int max_len() const override { return 1; } + + bool is_thread_safe() const override { return true; } + base_converter* clone() const override { return new simple_converter(*this); } + + uint32_t to_unicode(const char*& begin, const char* end) override { return cvt_.to_unicode(begin, end); } + uint32_t from_unicode(uint32_t u, char* begin, const char* end) override + { + return cvt_.from_unicode(u, begin, end); + } + + private: + simple_converter_impl cvt_; + }; + + template + class simple_codecvt : public generic_codecvt> { + public: + simple_codecvt(const std::string& encoding, size_t refs = 0) : + generic_codecvt>(refs), cvt_(encoding) + {} + + struct state_type {}; + static state_type initial_state(generic_codecvt_base::initial_convertion_state /* unused */) + { + return state_type(); + } + static int max_encoding_length() { return 1; } + + utf::code_point to_unicode(state_type&, const char*& begin, const char* end) const + { + return cvt_.to_unicode(begin, end); + } + + utf::code_point from_unicode(state_type&, utf::code_point u, char* begin, const char* end) const + { + return cvt_.from_unicode(u, begin, end); + } + + private: + simple_converter_impl cvt_; + }; + + namespace { + const char* simple_encoding_table[] = { + "cp1250", "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", + "cp1256", "cp1257", "iso88591", "iso885913", "iso885915", "iso88592", + "iso88593", "iso88594", "iso88595", "iso88596", "iso88597", "iso88598", + "iso88599", "koi8r", "koi8u", "usascii", "windows1250", "windows1251", + "windows1252", "windows1253", "windows1254", "windows1255", "windows1256", "windows1257"}; + + bool compare_strings(const char* l, const char* r) + { + return strcmp(l, r) < 0; + } + } // namespace + + bool check_is_simple_encoding(const std::string& encoding) + { + std::string norm = util::normalize_encoding(encoding); + return std::binary_search(simple_encoding_table, + simple_encoding_table + + sizeof(simple_encoding_table) / sizeof(const char*), + norm.c_str(), + compare_strings); + } + + std::unique_ptr create_simple_converter(const std::string& encoding) + { + return std::unique_ptr(create_simple_converter_new_ptr(encoding)); + } + base_converter* create_simple_converter_new_ptr(const std::string& encoding) + { + if(check_is_simple_encoding(encoding)) + return new simple_converter(encoding); + return 0; + } + + std::unique_ptr create_utf8_converter() + { + return std::unique_ptr(create_utf8_converter_new_ptr()); + } + + base_converter* create_utf8_converter_new_ptr() + { + return new utf8_converter(); + } + + template + class code_converter : public generic_codecvt> { + public: + typedef std::unique_ptr base_converter_ptr; + typedef base_converter_ptr state_type; + + code_converter(base_converter_ptr cvt, size_t refs = 0) : + generic_codecvt>(refs), cvt_(std::move(cvt)) + { + max_len_ = cvt_->max_len(); + thread_safe_ = cvt_->is_thread_safe(); + } + + int max_encoding_length() const { return max_len_; } + + base_converter_ptr initial_state(generic_codecvt_base::initial_convertion_state /* unused */) const + { + base_converter_ptr r; + if(!thread_safe_) + r.reset(cvt_->clone()); + return r; + } + + utf::code_point to_unicode(base_converter_ptr& ptr, const char*& begin, const char* end) const + { + if(thread_safe_) + return cvt_->to_unicode(begin, end); + else + return ptr->to_unicode(begin, end); + } + + utf::code_point from_unicode(base_converter_ptr& ptr, utf::code_point u, char* begin, const char* end) const + { + if(thread_safe_) + return cvt_->from_unicode(u, begin, end); + else + return ptr->from_unicode(u, begin, end); + } + + private: + base_converter_ptr cvt_; + int max_len_; + bool thread_safe_; + }; + + std::locale create_codecvt(const std::locale& in, std::unique_ptr cvt, char_facet_t type) + { + if(!cvt) + cvt.reset(new base_converter()); + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new code_converter(std::move(cvt))); + case char_facet_t::wchar_f: return std::locale(in, new code_converter(std::move(cvt))); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new code_converter(std::move(cvt))); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new code_converter(std::move(cvt))); +#endif + } + return in; + } + + /// Install utf8 codecvt to UTF-16 or UTF-32 into locale \a in and return + /// new locale that is based on \a in and uses new facet. + std::locale create_utf8_codecvt(const std::locale& in, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new utf8_codecvt()); + case char_facet_t::wchar_f: return std::locale(in, new utf8_codecvt()); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new utf8_codecvt()); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new utf8_codecvt()); +#endif + } + return in; + } + + /// This function installs codecvt that can be used for conversion between single byte + /// character encodings like ISO-8859-1, koi8-r, windows-1255 and Unicode code points, + /// + /// Throws invalid_charset_error if the character set is not supported or isn't single byte character + /// set + std::locale create_simple_codecvt(const std::locale& in, const std::string& encoding, char_facet_t type) + { + if(!check_is_simple_encoding(encoding)) + throw boost::locale::conv::invalid_charset_error("Invalid simple encoding " + encoding); + + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new simple_codecvt(encoding)); + case char_facet_t::wchar_f: return std::locale(in, new simple_codecvt(encoding)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new simple_codecvt(encoding)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new simple_codecvt(encoding)); +#endif + } + return in; + } + +}}} // namespace boost::locale::util diff --git a/libs/locale/src/boost/locale/util/default_locale.cpp b/libs/locale/src/boost/locale/util/default_locale.cpp new file mode 100644 index 0000000..d4ede78 --- /dev/null +++ b/libs/locale/src/boost/locale/util/default_locale.cpp @@ -0,0 +1,66 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +#if BOOST_LOCALE_USE_WIN32_API +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +#endif + +#if BOOST_LOCALE_USE_WIN32_API +// Get information about the user default locale and put it into the buffer. +// Return true on success +template +static bool get_user_default_locale_info(LCTYPE lcType, char (&buf)[N]) +{ + return GetLocaleInfoA(LOCALE_USER_DEFAULT, lcType, buf, N) != 0; +} +#endif + +namespace boost { namespace locale { namespace util { + std::string get_system_locale(bool use_utf8_on_windows) + { + const char* lang = 0; + if(!lang || !*lang) + lang = getenv("LC_ALL"); + if(!lang || !*lang) + lang = getenv("LC_CTYPE"); + if(!lang || !*lang) + lang = getenv("LANG"); +#if !BOOST_LOCALE_USE_WIN32_API + (void)use_utf8_on_windows; // not relevant for non-windows + if(!lang || !*lang) + lang = "C"; + return lang; +#else + if(lang && *lang) + return lang; + + char buf[10]{}; + if(!get_user_default_locale_info(LOCALE_SISO639LANGNAME, buf)) + return "C"; + std::string lc_name = buf; + if(get_user_default_locale_info(LOCALE_SISO3166CTRYNAME, buf)) { + lc_name += "_"; + lc_name += buf; + } + if(use_utf8_on_windows || !get_user_default_locale_info(LOCALE_IDEFAULTANSICODEPAGE, buf)) + lc_name += ".UTF-8"; + else { + if(atoi(buf) == 0) + lc_name += ".UTF-8"; + else + lc_name.append(".windows-").append(buf); + } + return lc_name; + +#endif + } +}}} // namespace boost::locale::util diff --git a/libs/locale/src/boost/locale/util/encoding.cpp b/libs/locale/src/boost/locale/util/encoding.cpp new file mode 100644 index 0000000..232fdaf --- /dev/null +++ b/libs/locale/src/boost/locale/util/encoding.cpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2022-2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/util/encoding.hpp" +#include "boost/locale/util/string.hpp" +#include +#if BOOST_LOCALE_USE_WIN32_API +# include "boost/locale/util/win_codepages.hpp" +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +#endif +#include +#include + +namespace boost { namespace locale { namespace util { + static std::string do_normalize_encoding(const char* encoding, const size_t len) + { + std::string result; + result.reserve(len); + for(char c = *encoding; c != 0; c = *(++encoding)) { + if(is_lower_ascii(c) || is_numeric_ascii(c)) + result += c; + else if(is_upper_ascii(c)) + result += char(c - 'A' + 'a'); + } + return result; + } + + std::string normalize_encoding(const std::string& encoding) + { + return do_normalize_encoding(encoding.c_str(), encoding.size()); + } + + std::string normalize_encoding(const char* encoding) + { + return do_normalize_encoding(encoding, std::strlen(encoding)); + } + +#if BOOST_LOCALE_USE_WIN32_API + static int normalized_encoding_to_windows_codepage(const std::string& encoding) + { + constexpr size_t n = sizeof(all_windows_encodings) / sizeof(all_windows_encodings[0]); + windows_encoding* begin = all_windows_encodings; + windows_encoding* end = all_windows_encodings + n; + + windows_encoding* ptr = std::lower_bound(begin, end, encoding.c_str()); + while(ptr != end && ptr->name == encoding) { + if(ptr->was_tested) + return ptr->codepage; + else if(IsValidCodePage(ptr->codepage)) { + // the thread safety is not an issue, maximum + // it would be checked more then once + ptr->was_tested = 1; + return ptr->codepage; + } else + ++ptr; + } + return -1; + } + + int encoding_to_windows_codepage(const char* encoding) + { + return normalized_encoding_to_windows_codepage(normalize_encoding(encoding)); + } + + int encoding_to_windows_codepage(const std::string& encoding) + { + return normalized_encoding_to_windows_codepage(normalize_encoding(encoding)); + } + +#endif +}}} // namespace boost::locale::util diff --git a/libs/locale/src/boost/locale/util/encoding.hpp b/libs/locale/src/boost/locale/util/encoding.hpp new file mode 100644 index 0000000..958233f --- /dev/null +++ b/libs/locale/src/boost/locale/util/encoding.hpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2022-2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_UTIL_ENCODING_HPP +#define BOOST_LOCALE_UTIL_ENCODING_HPP + +#include +#include +#include + +namespace boost { namespace locale { namespace util { + + /// Get the UTF encoding name of the given \a CharType + template + const char* utf_name() + { + constexpr auto CharSize = sizeof(CharType); + static_assert(CharSize == 1 || CharSize == 2 || CharSize == 4, "Unknown Character Encoding"); + switch(CharSize) { + case 1: return "UTF-8"; + case 2: { + const int16_t endianMark = 1; + const bool isLittleEndian = reinterpret_cast(&endianMark)[0] == 1; + return isLittleEndian ? "UTF-16LE" : "UTF-16BE"; + } + case 4: { + const int32_t endianMark = 1; + const bool isLittleEndian = reinterpret_cast(&endianMark)[0] == 1; + return isLittleEndian ? "UTF-32LE" : "UTF-32BE"; + } + } + BOOST_UNREACHABLE_RETURN("Unknown UTF"); + } + + /// Make encoding lowercase and remove all non-alphanumeric characters + BOOST_LOCALE_DECL std::string normalize_encoding(const std::string& encoding); + BOOST_LOCALE_DECL std::string normalize_encoding(const char* encoding); + /// True if the normalized encodings are equal + inline bool are_encodings_equal(const std::string& l, const std::string& r) + { + return normalize_encoding(l) == normalize_encoding(r); + } + +#if BOOST_LOCALE_USE_WIN32_API + int encoding_to_windows_codepage(const char* encoding); + int encoding_to_windows_codepage(const std::string& encoding); +#endif + +}}} // namespace boost::locale::util + +#endif diff --git a/libs/locale/src/boost/locale/util/gregorian.cpp b/libs/locale/src/boost/locale/util/gregorian.cpp new file mode 100644 index 0000000..5c74b54 --- /dev/null +++ b/libs/locale/src/boost/locale/util/gregorian.cpp @@ -0,0 +1,716 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/util/gregorian.hpp" +#include +#include +#include +#include "boost/locale/util/timezone.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace util { + namespace { + + int is_leap(int year) + { + if(year % 400 == 0) + return 1; + if(year % 100 == 0) + return 0; + if(year % 4 == 0) + return 1; + return 0; + } + + int days_in_month(int year, int month) + { + constexpr int tbl[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; + return tbl[is_leap(year)][month - 1]; + } + + constexpr int days_from_0_impl(int year_m1) + { + return 365 * year_m1 + (year_m1 / 400) - (year_m1 / 100) + (year_m1 / 4); + } + constexpr int days_from_0(int year) + { + return days_from_0_impl(year - 1); + } + + constexpr int days_from_0_to_1970 = days_from_0(1970); + constexpr int days_from_1970(int year) + { + return days_from_0(year) - days_from_0_to_1970; + } + + int days_from_1jan(int year, int month, int day) + { + constexpr int days[2][12] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}}; + return days[is_leap(year)][month - 1] + day - 1; + } + + std::time_t internal_timegm(const std::tm* t) + { + int year = t->tm_year + 1900; + int month = t->tm_mon; + if(month > 11) { + year += month / 12; + month %= 12; + } else if(month < 0) { + int years_diff = (-month + 11) / 12; + year -= years_diff; + month += 12 * years_diff; + } + month++; + int day = t->tm_mday; + int day_of_year = days_from_1jan(year, month, day); + int days_since_epoch = days_from_1970(year) + day_of_year; + + std::time_t seconds_in_day = 3600 * 24; + std::time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec; + + return result; + } + + } // namespace + + namespace { + + // Locale dependent data + + bool comparator(const char* left, const char* right) + { + return strcmp(left, right) < 0; + } + + // Ref: CLDR 1.9 common/supplemental/supplementalData.xml + // + // monday - default + // fri - MV + // sat - AE AF BH DJ DZ EG ER ET IQ IR JO KE KW LY MA OM QA SA SD SO SY TN YE + // sun - AR AS AZ BW CA CN FO GE GL GU HK IL IN JM JP KG KR LA MH MN MO MP MT NZ PH PK SG TH TT TW UM US UZ VI + // ZW + + int first_day_of_week(const char* terr) + { + constexpr const char* sat[] = {"AE", "AF", "BH", "DJ", "DZ", "EG", "ER", "ET", "IQ", "IR", "JO", "KE", + "KW", "LY", "MA", "OM", "QA", "SA", "SD", "SO", "SY", "TN", "YE"}; + constexpr const char* sunday[] = {"AR", "AS", "AZ", "BW", "CA", "CN", "FO", "GE", "GL", "GU", "HK", "IL", + "IN", "JM", "JP", "KG", "KR", "LA", "MH", "MN", "MO", "MP", "MT", "NZ", + "PH", "PK", "SG", "TH", "TT", "TW", "UM", "US", "UZ", "VI", "ZW"}; + if(strcmp(terr, "MV") == 0) + return 5; // fri + if(std::binary_search(sat, sat + sizeof(sat) / (sizeof(sat[0])), terr, comparator)) + return 6; // sat + if(std::binary_search(sunday, + sunday + sizeof(sunday) / (sizeof(sunday[0])), + terr, + comparator)) + return 0; // sun + // default + return 1; // mon + } + } // namespace + + class gregorian_calendar : public abstract_calendar { + public: + gregorian_calendar(const std::string& terr) + { + first_day_of_week_ = first_day_of_week(terr.c_str()); + time_ = std::time(0); + is_local_ = true; + tzoff_ = 0; + from_time(time_); + } + + /// Make a polymorphic copy of the calendar + gregorian_calendar* clone() const override { return new gregorian_calendar(*this); } + + /// Set specific \a value for period \a p, note not all values are settable. + void set_value(period::marks::period_mark m, int value) override + { + using namespace period::marks; + switch(m) { + case era: ///< Era i.e. AC, BC in Gregorian and Julian calendar, range [0,1] + return; + case year: ///< Year, it is calendar specific + case extended_year: ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1. + tm_updated_.tm_year = value - 1900; + break; + case month: tm_updated_.tm_mon = value; break; + case day: tm_updated_.tm_mday = value; break; + case hour: ///< 24 clock hour [0..23] + tm_updated_.tm_hour = value; + break; + case hour_12: ///< 12 clock hour [0..11] + tm_updated_.tm_hour = tm_updated_.tm_hour / 12 * 12 + value; + break; + case am_pm: ///< am or pm marker, [0..1] + tm_updated_.tm_hour = 12 * value + tm_updated_.tm_hour % 12; + break; + case minute: ///< minute [0..59] + tm_updated_.tm_min = value; + break; + case second: tm_updated_.tm_sec = value; break; + case day_of_year: + normalize(); + tm_updated_.tm_mday += (value - (tm_updated_.tm_yday + 1)); + break; + case day_of_week: ///< Day of week, starting from Sunday, [1..7] + if(value < 1) // make sure it is positive + value += (-value / 7) * 7 + 7; + // convert to local DOW + value = (value - 1 - first_day_of_week_ + 14) % 7 + 1; + BOOST_FALLTHROUGH; + case day_of_week_local: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, + ///< [1..7] + normalize(); + tm_updated_.tm_mday += (value - 1) - (tm_updated_.tm_wday - first_day_of_week_ + 7) % 7; + break; + case day_of_week_in_month: ///< Original number of the day of the week in month. (1st sunday, 2nd sunday + ///< etc) + case week_of_year: ///< The week number in the year, 4 is the minimal number of days to be in month + case week_of_month: ///< The week number within current month + { + normalize(); + int current_week = get_value(m, current); + int diff = 7 * (value - current_week); + tm_updated_.tm_mday += diff; + } break; + case period::marks::first_day_of_week: ///< For example Sunday in US, Monday in France + case invalid: return; + } + normalized_ = false; + } + + void normalize() override + { + if(!normalized_) { + std::tm val = tm_updated_; + val.tm_isdst = -1; + val.tm_wday = -1; // indicator of error + std::time_t point = -1; + if(is_local_) { + point = std::mktime(&val); + if(point == static_cast(-1)) { +#ifndef BOOST_WINDOWS + // windows does not handle negative time_t, under other plaforms + // it may be actually valid value in 1969-12-31 23:59:59 + // so we check that a filed was updated - does not happen in case of error + if(val.tm_wday == -1) +#endif + { + throw date_time_error("boost::locale::gregorian_calendar: invalid time"); + } + } + } else { + point = internal_timegm(&val); +#ifdef BOOST_WINDOWS + // Windows uses TLS, thread safe + std::tm* revert_point = 0; + if(point < 0 || (revert_point = gmtime(&point)) == 0) + throw date_time_error("boost::locale::gregorian_calendar time is out of range"); + val = *revert_point; +#else + if(!gmtime_r(&point, &val)) + throw date_time_error("boost::locale::gregorian_calendar invalid time"); +#endif + } + + time_ = point - tzoff_; + tm_ = val; + tm_updated_ = val; + normalized_ = true; + } + } + + int get_week_number(int day, int wday) const + { + /// This is the number of days that are considered within + /// period such that the week belongs there + constexpr int days_in_full_week = 4; + + // Always use local week start + int current_dow = (wday - first_day_of_week_ + 7) % 7; + // Calculate local week day of Jan 1st. + int first_week_day = (current_dow + 700 - day) % 7; + // adding something big dividable by 7 + + int start_of_period_in_weeks; + if(first_week_day < days_in_full_week) { + start_of_period_in_weeks = -first_week_day; + } else { + start_of_period_in_weeks = 7 - first_week_day; + } + int week_number_in_days = day - start_of_period_in_weeks; + if(week_number_in_days < 0) + return -1; + return week_number_in_days / 7 + 1; + } + + /// Get specific value for period \a p according to a value_type \a v + int get_value(period::marks::period_mark m, value_type v) const override + { + using namespace period::marks; + switch(m) { + case era: return 1; + case year: + case extended_year: + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: +#ifdef BOOST_WINDOWS + return 1970; // Unix epoch windows can't handle negative time_t +#else + if(sizeof(std::time_t) == 4) + return 1901; // minimal year with 32 bit time_t + else + return 1; +#endif + case absolute_maximum: + case least_maximum: + case actual_maximum: + BOOST_LOCALE_START_CONST_CONDITION + if(sizeof(std::time_t) == 4) + return 2038; // Y2K38 - maximal with 32 bit time_t + else + return std::numeric_limits::max() + / 365; // Reasonably large but we can still get the days without overflowing + BOOST_LOCALE_END_CONST_CONDITION + case current: return tm_.tm_year + 1900; + }; + break; + case month: + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 0; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 11; + case current: return tm_.tm_mon; + }; + break; + case day: + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: return 31; + case least_maximum: return 28; + case actual_maximum: return days_in_month(tm_.tm_year + 1900, tm_.tm_mon + 1); + case current: return tm_.tm_mday; + }; + break; + case day_of_year: ///< The number of day in year, starting from 1 + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: return 366; + case least_maximum: return 365; + case actual_maximum: return is_leap(tm_.tm_year + 1900) ? 366 : 365; + case current: return tm_.tm_yday + 1; + } + break; + case day_of_week: ///< Day of week, starting from Sunday, [1..7] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 7; + case current: return tm_.tm_wday + 1; + } + break; + case day_of_week_local: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, + ///< [1..7] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 7; + case current: return (tm_.tm_wday - first_day_of_week_ + 7) % 7 + 1; + } + break; + case hour: ///< 24 clock hour [0..23] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 0; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 23; + case current: return tm_.tm_hour; + } + break; + case hour_12: ///< 12 clock hour [0..11] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 0; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 11; + case current: return tm_.tm_hour % 12; + } + break; + case am_pm: ///< am or pm marker, [0..1] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 0; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 1; + case current: return tm_.tm_hour >= 12 ? 1 : 0; + } + break; + case minute: ///< minute [0..59] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 0; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 59; + case current: return tm_.tm_min; + } + break; + case second: ///< second [0..59] + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 0; + case absolute_maximum: + case least_maximum: + case actual_maximum: return 59; + case current: return tm_.tm_sec; + } + break; + case period::marks::first_day_of_week: ///< For example Sunday in US, Monday in France + return first_day_of_week_ + 1; + + case week_of_year: ///< The week number in the year + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: return 53; + case least_maximum: return 52; + case actual_maximum: { + int year = tm_.tm_year + 1900; + int end_of_year_days = (is_leap(year) ? 366 : 365) - 1; + int dow_of_end_of_year = (end_of_year_days - tm_.tm_yday + tm_.tm_wday) % 7; + return get_week_number(end_of_year_days, dow_of_end_of_year); + } + case current: { + int val = get_week_number(tm_.tm_yday, tm_.tm_wday); + if(val < 0) + return 53; + return val; + } + } + break; + case week_of_month: ///< The week number within current month + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: return 5; + case least_maximum: return 4; + case actual_maximum: { + int end_of_month_days = days_in_month(tm_.tm_year + 1900, tm_.tm_mon + 1); + int dow_of_end_of_month = (end_of_month_days - tm_.tm_mday + tm_.tm_wday) % 7; + return get_week_number(end_of_month_days, dow_of_end_of_month); + } + case current: { + int val = get_week_number(tm_.tm_mday, tm_.tm_wday); + if(val < 0) + return 5; + return val; + } + } + break; + case day_of_week_in_month: ///< Original number of the day of the week in month. + switch(v) { + case absolute_minimum: + case greatest_minimum: + case actual_minimum: return 1; + case absolute_maximum: return 5; + case least_maximum: return 4; + case actual_maximum: + if(tm_.tm_mon == 1 && !is_leap(tm_.tm_year + 1900)) { + // only february in non leap year is 28 days, the rest + // has more then 4 weeks + return 4; + } + return 5; + case current: return (tm_.tm_mday - 1) / 7 + 1; + } + break; + case invalid: BOOST_ASSERT_MSG(false, "Shouldn't use 'invalid' value."); break; + } + return 0; + } + + /// Set current time point + void set_time(const posix_time& p) override + { + from_time(static_cast(p.seconds)); + } + posix_time get_time() const override + { + posix_time pt = {time_, 0}; + return pt; + } + double get_time_ms() const override + { + return time_ * 1e3; + } + + /// Set option for calendar, for future use + void set_option(calendar_option_type opt, int /*v*/) override + { + switch(opt) { + case is_gregorian: throw date_time_error("is_gregorian is not settable options for calendar"); + case is_dst: throw date_time_error("is_dst is not settable options for calendar"); + } + } + /// Get option for calendar, currently only check if it is Gregorian calendar + int get_option(calendar_option_type opt) const override + { + switch(opt) { + case is_gregorian: return 1; + case is_dst: return tm_.tm_isdst == 1; + } + return 0; + } + + /// Adjust period's \a p value by \a difference items using a update_type \a u. + /// Note: not all values are adjustable + void adjust_value(period::marks::period_mark m, update_type u, int difference) override + { + switch(u) { + case move: { + using namespace period::marks; + switch(m) { + case year: ///< Year, it is calendar specific + case extended_year: ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == + ///< -1. + tm_updated_.tm_year += difference; + break; + case month: tm_updated_.tm_mon += difference; break; + case day: + case day_of_year: + case day_of_week: ///< Day of week, starting from Sunday, [1..7] + case day_of_week_local: ///< Local day of week, for example in France Monday is 1, in US Sunday + ///< is 1, [1..7] + tm_updated_.tm_mday += difference; + break; + case hour: ///< 24 clock hour [0..23] + case hour_12: ///< 12 clock hour [0..11] + tm_updated_.tm_hour += difference; + break; + case am_pm: ///< am or pm marker, [0..1] + tm_updated_.tm_hour += 12 * difference; + break; + case minute: ///< minute [0..59] + tm_updated_.tm_min += difference; + break; + case second: tm_updated_.tm_sec += difference; break; + case week_of_year: ///< The week number in the year + case week_of_month: ///< The week number within current month + case day_of_week_in_month: ///< Original number of the day of the week in month. + tm_updated_.tm_mday += difference * 7; + break; + case era: + case period::marks::first_day_of_week: + case invalid: break; // Not adjustable and ignored + } + normalized_ = false; + normalize(); + } break; + case roll: { + const int cur_min = get_value(m, actual_minimum); + const int cur_max = get_value(m, actual_maximum); + BOOST_ASSERT(cur_max >= cur_min); + const int range = cur_max - cur_min + 1; + int value = get_value(m, current) - cur_min; + BOOST_ASSERT(value >= 0 && value < range); + BOOST_ASSERT_MSG(difference <= std::numeric_limits::max(), "Input is to large"); + value = (value + difference) % range; + // If the sum above was negative the result of the modulo operation "can" be negative too. + if(value < 0) + value += range; + BOOST_ASSERT(value >= 0 && value < range); + set_value(m, value + cur_min); + normalize(); + } + } + } + + int get_diff(period::marks::period_mark m, int diff, const gregorian_calendar* other) const + { + if(diff == 0) + return 0; + hold_ptr self(clone()); + self->adjust_value(m, move, diff); + if(diff > 0) { + if(self->time_ > other->time_) + return diff - 1; + else + return diff; + } else { + if(self->time_ < other->time_) + return diff + 1; + else + return diff; + } + } + + /// Calculate the difference between this calendar and \a other in \a p units + int difference(const abstract_calendar& other_cal, period::marks::period_mark m) const override + { + hold_ptr keeper; + const gregorian_calendar* other = dynamic_cast(&other_cal); + if(!other) { + keeper.reset(clone()); + keeper->set_time(other_cal.get_time()); + other = keeper.get(); + } + + int factor = 1; // for weeks vs days handling + + using namespace period::marks; + switch(m) { + case era: return 0; + case year: + case extended_year: { + int diff = other->tm_.tm_year - tm_.tm_year; + return get_diff(period::marks::year, diff, other); + } + case month: { + int diff = 12 * (other->tm_.tm_year - tm_.tm_year) + other->tm_.tm_mon - tm_.tm_mon; + return get_diff(period::marks::month, diff, other); + } + case day_of_week_in_month: + case week_of_month: + case week_of_year: factor = 7; BOOST_FALLTHROUGH; + case day: + case day_of_year: + case day_of_week: + case day_of_week_local: { + int diff = other->tm_.tm_yday - tm_.tm_yday; + if(other->tm_.tm_year != tm_.tm_year) { + diff += days_from_0(other->tm_.tm_year + 1900) - days_from_0(tm_.tm_year + 1900); + } + return get_diff(period::marks::day, diff, other) / factor; + } + case am_pm: return static_cast((other->time_ - time_) / (3600 * 12)); + case hour: + case hour_12: return static_cast((other->time_ - time_) / 3600); + case minute: return static_cast((other->time_ - time_) / 60); + case second: return static_cast(other->time_ - time_); + case invalid: + case period::marks::first_day_of_week: break; // Not adjustable + } + return 0; + } + + /// Set time zone, empty - use system + void set_timezone(const std::string& tz) override + { + if(tz.empty()) { + is_local_ = true; + tzoff_ = 0; + } else { + is_local_ = false; + tzoff_ = parse_tz(tz); + } + from_time(time_); + time_zone_name_ = tz; + } + std::string get_timezone() const override + { + return time_zone_name_; + } + + bool same(const abstract_calendar* other) const override + { + const gregorian_calendar* gcal = dynamic_cast(other); + if(!gcal) + return false; + return gcal->tzoff_ == tzoff_ && gcal->is_local_ == is_local_ + && gcal->first_day_of_week_ == first_day_of_week_; + } + + private: + void from_time(std::time_t point) + { + std::time_t real_point = point + tzoff_; + std::tm* t = 0; +#ifdef BOOST_WINDOWS + // Windows uses TLS, thread safe + t = is_local_ ? localtime(&real_point) : gmtime(&real_point); +#else + std::tm tmp_tm; + t = is_local_ ? localtime_r(&real_point, &tmp_tm) : gmtime_r(&real_point, &tmp_tm); +#endif + if(!t) { + throw date_time_error("boost::locale::gregorian_calendar: invalid time point"); + } + tm_ = *t; + tm_updated_ = *t; + normalized_ = true; + time_ = point; + } + int first_day_of_week_; + std::time_t time_; + std::tm tm_; + std::tm tm_updated_; + bool normalized_; + bool is_local_; + int tzoff_; + std::string time_zone_name_; + }; + + abstract_calendar* create_gregorian_calendar(const std::string& terr) + { + return new gregorian_calendar(terr); + } + + class gregorian_facet : public calendar_facet { + public: + gregorian_facet(const std::string& terr, size_t refs = 0) : calendar_facet(refs), terr_(terr) {} + abstract_calendar* create_calendar() const override { return create_gregorian_calendar(terr_); } + + private: + std::string terr_; + }; + + std::locale install_gregorian_calendar(const std::locale& in, const std::string& terr) + { + return std::locale(in, new gregorian_facet(terr)); + } + +}}} // namespace boost::locale::util + +// boostinspect:nominmax diff --git a/libs/locale/src/boost/locale/util/gregorian.hpp b/libs/locale/src/boost/locale/util/gregorian.hpp new file mode 100644 index 0000000..e3a3a46 --- /dev/null +++ b/libs/locale/src/boost/locale/util/gregorian.hpp @@ -0,0 +1,18 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_SRC_UTIL_GREGORIAN_HPP +#define BOOST_LOCALE_SRC_UTIL_GREGORIAN_HPP + +#include + +namespace boost { namespace locale { namespace util { + + std::locale install_gregorian_calendar(const std::locale& in, const std::string& terr); + +}}} // namespace boost::locale::util + +#endif diff --git a/libs/locale/src/boost/locale/util/iconv.hpp b/libs/locale/src/boost/locale/util/iconv.hpp new file mode 100644 index 0000000..25701f7 --- /dev/null +++ b/libs/locale/src/boost/locale/util/iconv.hpp @@ -0,0 +1,92 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_ICONV_FIXER_HPP +#define BOOST_LOCALE_ICONV_FIXER_HPP + +#include +#include + +namespace boost { namespace locale { + class iconv_handle { + iconv_t h_; + void close() + { + if(*this) + iconv_close(h_); + } + + public: + iconv_handle(iconv_t h = iconv_t(-1)) : h_(h) {} + iconv_handle(iconv_handle&& rhs) noexcept : h_(exchange(rhs.h_, iconv_t(-1))) {} + iconv_handle& operator=(iconv_handle&& rhs) noexcept + { + h_ = exchange(rhs.h_, iconv_t(-1)); + return *this; + } + iconv_handle& operator=(iconv_t h) + { + close(); + h_ = h; + return *this; + } + ~iconv_handle() { close(); } + + operator iconv_t() const { return h_; } + explicit operator bool() const { return h_ != iconv_t(-1); } + }; + + extern "C" { +#if defined(__ICONV_F_HIDE_INVALID) && defined(__FreeBSD__) +# define BOOST_LOCALE_ICONV_FUNC __iconv +# define BOOST_LOCALE_ICONV_FLAGS , __ICONV_F_HIDE_INVALID, 0 + + // GNU variant + typedef size_t (*const_iconv_ptr_type)(iconv_t, const char**, size_t*, char**, size_t*, uint32_t, size_t*); + // POSIX variant + typedef size_t (*nonconst_iconv_ptr_type)(iconv_t, char**, size_t*, char**, size_t*, uint32_t, size_t*); +#else +# define BOOST_LOCALE_ICONV_FUNC iconv +# define BOOST_LOCALE_ICONV_FLAGS + + typedef size_t (*const_iconv_ptr_type)(iconv_t, const char**, size_t*, char**, size_t*); + typedef size_t (*nonconst_iconv_ptr_type)(iconv_t, char**, size_t*, char**, size_t*); +#endif + } + + inline size_t + call_iconv_impl(const_iconv_ptr_type ptr, iconv_t d, const char** in, size_t* insize, char** out, size_t* outsize) + { + return ptr(d, in, insize, out, outsize BOOST_LOCALE_ICONV_FLAGS); + } + inline size_t call_iconv_impl(nonconst_iconv_ptr_type ptr, + iconv_t d, + const char** in, + size_t* insize, + char** out, + size_t* outsize) + { + return ptr(d, const_cast(in), insize, out, outsize BOOST_LOCALE_ICONV_FLAGS); + } + + inline size_t call_iconv(iconv_t d, const char** in, size_t* insize, char** out, size_t* outsize) + { + return call_iconv_impl(BOOST_LOCALE_ICONV_FUNC, d, in, insize, out, outsize); + } + + // Convenience overload when the adjusted in/out ptrs are not required + inline size_t call_iconv(iconv_t d, const char* in, size_t* insize, char* out, size_t* outsize) + { + return call_iconv(d, &in, insize, &out, outsize); + } + // Disambiguation + inline size_t call_iconv(iconv_t d, std::nullptr_t, std::nullptr_t, std::nullptr_t, std::nullptr_t) + { + return call_iconv_impl(BOOST_LOCALE_ICONV_FUNC, d, nullptr, nullptr, nullptr, nullptr); + } +}} // namespace boost::locale + +#endif diff --git a/libs/locale/src/boost/locale/util/info.cpp b/libs/locale/src/boost/locale/util/info.cpp new file mode 100644 index 0000000..65f8167 --- /dev/null +++ b/libs/locale/src/boost/locale/util/info.cpp @@ -0,0 +1,52 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace util { + + class simple_info : public info { + public: + simple_info(const std::string& name, size_t refs = 0) : info(refs), name_(name) { d.parse(name); } + std::string get_string_property(string_propery v) const override + { + switch(v) { + case language_property: return d.language(); + case country_property: return d.country(); + case variant_property: return d.variant(); + case encoding_property: return d.encoding(); + case name_property: return name_; + } + return ""; + } + + int get_integer_property(integer_property v) const override + { + switch(v) { + case utf8_property: return d.is_utf8(); + } + return 0; + } + + private: + locale_data d; + std::string name_; + }; + + std::locale create_info(const std::locale& in, const std::string& name) + { + return std::locale(in, new simple_info(name)); + } + +}}} // namespace boost::locale::util diff --git a/libs/locale/src/boost/locale/util/locale_data.cpp b/libs/locale/src/boost/locale/util/locale_data.cpp new file mode 100644 index 0000000..113d27f --- /dev/null +++ b/libs/locale/src/boost/locale/util/locale_data.cpp @@ -0,0 +1,160 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2022-2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include "boost/locale/encoding/conv.hpp" +#include "boost/locale/util/encoding.hpp" +#include "boost/locale/util/string.hpp" +#include +#include +#include +#include + +namespace boost { namespace locale { namespace util { + locale_data::locale_data() + { + reset(); + } + + locale_data::locale_data(const std::string& locale_name) + { + if(!parse(locale_name)) + throw std::invalid_argument("Failed to parse locale name: " + locale_name); + } + + void locale_data::reset() + { + language_ = "C"; + country_.clear(); + encoding_ = "US-ASCII"; + variant_.clear(); + utf8_ = false; + } + + std::string locale_data::to_string() const + { + std::string result = language_; + if(!country_.empty()) + (result += '_') += country_; + if(!encoding_.empty() && !util::are_encodings_equal(encoding_, "US-ASCII")) + (result += '.') += encoding_; + if(!variant_.empty()) + (result += '@') += variant_; + return result; + } + + bool locale_data::parse(const std::string& locale_name) + { + reset(); + return parse_from_lang(locale_name); + } + + bool locale_data::parse_from_lang(const std::string& input) + { + const auto end = input.find_first_of("-_@."); + std::string tmp = input.substr(0, end); + if(tmp.empty()) + return false; + // lowercase ASCII + for(char& c : tmp) { + if(is_upper_ascii(c)) + c += 'a' - 'A'; + else if(!is_lower_ascii(c)) + return false; + } + if(tmp != "c" && tmp != "posix") // Keep default + language_ = tmp; + + if(end >= input.size()) + return true; + else if(input[end] == '-' || input[end] == '_') + return parse_from_country(input.substr(end + 1)); + else if(input[end] == '.') + return parse_from_encoding(input.substr(end + 1)); + else { + BOOST_ASSERT_MSG(input[end] == '@', "Unexpected delimiter"); + return parse_from_variant(input.substr(end + 1)); + } + } + + bool locale_data::parse_from_country(const std::string& input) + { + if(language_ == "C") + return false; + + const auto end = input.find_first_of("@."); + std::string tmp = input.substr(0, end); + if(tmp.empty()) + return false; + + // Make uppercase + for(char& c : tmp) { + if(util::is_lower_ascii(c)) + c += 'A' - 'a'; + } + // If it's ALL uppercase ASCII, assume ISO 3166 country id + if(std::find_if_not(tmp.begin(), tmp.end(), util::is_upper_ascii) != tmp.end()) { + // else handle special cases: + // - en_US_POSIX is an alias for C + // - M49 country code: 3 digits + if(language_ == "en" && tmp == "US_POSIX") { + language_ = "C"; + tmp.clear(); + } else if(tmp.size() != 3u || std::find_if_not(tmp.begin(), tmp.end(), util::is_numeric_ascii) != tmp.end()) + return false; + } + + country_ = tmp; + if(end >= input.size()) + return true; + else if(input[end] == '.') + return parse_from_encoding(input.substr(end + 1)); + else { + BOOST_ASSERT_MSG(input[end] == '@', "Unexpected delimiter"); + return parse_from_variant(input.substr(end + 1)); + } + } + + bool locale_data::parse_from_encoding(const std::string& input) + { + const auto end = input.find_first_of('@'); + std::string tmp = input.substr(0, end); + if(tmp.empty()) + return false; + // No assumptions, but uppercase + for(char& c : tmp) { + if(util::is_lower_ascii(c)) + c += 'A' - 'a'; + } + encoding_ = tmp; + + utf8_ = util::normalize_encoding(encoding_) == "utf8"; + + if(end >= input.size()) + return true; + else { + BOOST_ASSERT_MSG(input[end] == '@', "Unexpected delimiter"); + return parse_from_variant(input.substr(end + 1)); + } + } + + bool locale_data::parse_from_variant(const std::string& input) + { + if(language_ == "C") + return false; + if(input.empty()) + return false; + variant_ = input; + // No assumptions, just make it lowercase + for(char& c : variant_) { + if(util::is_upper_ascii(c)) + c += 'a' - 'A'; + } + return true; + } + +}}} // namespace boost::locale::util diff --git a/libs/locale/src/boost/locale/util/numeric.hpp b/libs/locale/src/boost/locale/util/numeric.hpp new file mode 100644 index 0000000..5b4393d --- /dev/null +++ b/libs/locale/src/boost/locale/util/numeric.hpp @@ -0,0 +1,383 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_UTIL_NUMERIC_HPP +#define BOOST_LOCALE_IMPL_UTIL_NUMERIC_HPP +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "boost/locale/util/timezone.hpp" + +namespace boost { namespace locale { namespace util { + + template + struct formatting_size_traits { + static size_t size(const std::basic_string& s, const std::locale& /*l*/) { return s.size(); } + }; + + template<> + struct formatting_size_traits { + static size_t size(const std::string& s, const std::locale& l) + { + if(!std::has_facet(l)) + return s.size(); + if(!std::use_facet(l).utf8()) + return s.size(); + // count code points, poor man's text size + size_t res = 0; + for(size_t i = 0; i < s.size(); i++) { + unsigned char c = s[i]; + if(c <= 127) + res++; + else if((c & 0xC0) == 0xC0) { // first UTF-8 byte + res++; + } + } + return res; + } + }; + + template + class base_num_format : public std::num_put { + public: + typedef typename std::num_put::iter_type iter_type; + typedef std::basic_string string_type; + typedef CharType char_type; + + base_num_format(size_t refs = 0) : std::num_put(refs) {} + + protected: + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, long val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, unsigned long val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, double val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, long double val) const override + { + return do_real_put(out, ios, fill, val); + } + + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, long long val) const override + { + return do_real_put(out, ios, fill, val); + } + iter_type do_put(iter_type out, std::ios_base& ios, char_type fill, unsigned long long val) const override + { + return do_real_put(out, ios, fill, val); + } + + private: + template + iter_type do_real_put(iter_type out, std::ios_base& ios, char_type fill, ValueType val) const + { + typedef std::num_put super; + + ios_info& info = ios_info::get(ios); + + switch(info.display_flags()) { + case flags::posix: { + typedef std::basic_ostringstream sstream_type; + sstream_type ss; + ss.imbue(std::locale::classic()); + ss.flags(ios.flags()); + ss.precision(ios.precision()); + ss.width(ios.width()); + iter_type ret_ptr = super::do_put(out, ss, fill, val); + ios.width(0); + return ret_ptr; + } + case flags::date: return format_time(out, ios, fill, static_cast(val), 'x'); + case flags::time: return format_time(out, ios, fill, static_cast(val), 'X'); + case flags::datetime: return format_time(out, ios, fill, static_cast(val), 'c'); + case flags::strftime: + return format_time(out, + ios, + fill, + static_cast(val), + info.date_time_pattern()); + case flags::currency: { + bool nat = info.currency_flags() == flags::currency_default + || info.currency_flags() == flags::currency_national; + bool intl = !nat; + return do_format_currency(intl, out, ios, fill, static_cast(val)); + } + + case flags::number: + case flags::percent: + case flags::spellout: + case flags::ordinal: break; + } + return super::do_put(out, ios, fill, val); + } + + virtual iter_type + do_format_currency(bool intl, iter_type out, std::ios_base& ios, char_type fill, long double val) const + { + if(intl) + return format_currency(out, ios, fill, val); + else + return format_currency(out, ios, fill, val); + } + + template + iter_type format_currency(iter_type out, std::ios_base& ios, char_type fill, long double val) const + { + std::locale loc = ios.getloc(); + int digits = std::use_facet>(loc).frac_digits(); + while(digits > 0) { + val *= 10; + digits--; + } + std::ios_base::fmtflags f = ios.flags(); + ios.flags(f | std::ios_base::showbase); + out = std::use_facet>(loc).put(out, intl, ios, fill, val); + ios.flags(f); + return out; + } + + iter_type format_time(iter_type out, std::ios_base& ios, char_type fill, std::time_t time, char c) const + { + string_type fmt; + fmt += char_type('%'); + fmt += char_type(c); + return format_time(out, ios, fill, time, fmt); + } + + iter_type format_time(iter_type out, + std::ios_base& ios, + char_type fill, + std::time_t time, + const string_type& format) const + { + std::string tz = ios_info::get(ios).time_zone(); + std::tm tm; +#if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__) + std::vector tmp_buf(tz.c_str(), tz.c_str() + tz.size() + 1); +#endif + if(tz.empty()) { +#ifdef BOOST_WINDOWS + /// Windows uses TLS + tm = *localtime(&time); +#else + localtime_r(&time, &tm); +#endif + } else { + int gmtoff = parse_tz(tz); + time += gmtoff; +#ifdef BOOST_WINDOWS + /// Windows uses TLS + tm = *gmtime(&time); +#else + gmtime_r(&time, &tm); +#endif + +#if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__) + // These have extra fields to specify timezone + if(gmtoff != 0) { + // bsd and apple want tm_zone be non-const + tm.tm_zone = &tmp_buf.front(); + tm.tm_gmtoff = gmtoff; + } +#endif + } + std::basic_ostringstream tmp_out; + std::use_facet>(ios.getloc()) + .put(tmp_out, tmp_out, fill, &tm, format.c_str(), format.c_str() + format.size()); + string_type str = tmp_out.str(); + std::streamsize on_left = 0, on_right = 0; + std::streamsize points = formatting_size_traits::size(str, ios.getloc()); + if(points < ios.width()) { + std::streamsize n = ios.width() - points; + + std::ios_base::fmtflags flags = ios.flags() & std::ios_base::adjustfield; + + // we do not really know internal point, so we assume that it does not + // exist. so according to the standard field should be right aligned + if(flags != std::ios_base::left) + on_left = n; + on_right = n - on_left; + } + while(on_left > 0) { + *out++ = fill; + on_left--; + } + std::copy(str.begin(), str.end(), out); + while(on_right > 0) { + *out++ = fill; + on_right--; + } + ios.width(0); + return out; + } + + }; /// num_format + + template + class base_num_parse : public std::num_get { + public: + base_num_parse(size_t refs = 0) : std::num_get(refs) {} + + protected: + typedef typename std::num_get::iter_type iter_type; + typedef std::basic_string string_type; + typedef CharType char_type; + + iter_type + do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned short& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned int& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type + do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, float& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type + do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, double& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + long double& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + long long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + iter_type do_get(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + unsigned long long& val) const override + { + return do_real_get(in, end, ios, err, val); + } + + private: + template + iter_type + do_real_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, ValueType& val) const + { + typedef std::num_get super; + + ios_info& info = ios_info::get(ios); + + switch(info.display_flags()) { + case flags::posix: { + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss.flags(ios.flags()); + ss.precision(ios.precision()); + return super::do_get(in, end, ss, err, val); + } + case flags::currency: { + long double ret_val = 0; + if(info.currency_flags() == flags::currency_default + || info.currency_flags() == flags::currency_national) + in = parse_currency(in, end, ios, err, ret_val); + else + in = parse_currency(in, end, ios, err, ret_val); + if(!(err & std::ios_base::failbit)) + val = static_cast(ret_val); + return in; + } + + // date-time parsing is not supported + // due to buggy standard + case flags::date: + case flags::time: + case flags::datetime: + case flags::strftime: + + case flags::number: + case flags::percent: + case flags::spellout: + case flags::ordinal: break; + } + return super::do_get(in, end, ios, err, val); + } + + template + iter_type parse_currency(iter_type in, + iter_type end, + std::ios_base& ios, + std::ios_base::iostate& err, + long double& val) const + { + std::locale loc = ios.getloc(); + int digits = std::use_facet>(loc).frac_digits(); + long double rval; + in = std::use_facet>(loc).get(in, end, intl, ios, err, rval); + if(!(err & std::ios::failbit)) { + while(digits > 0) { + rval /= 10; + digits--; + } + val = rval; + } + return in; + } + }; + +}}} // namespace boost::locale::util + +#endif diff --git a/libs/locale/src/boost/locale/util/timezone.hpp b/libs/locale/src/boost/locale/util/timezone.hpp new file mode 100644 index 0000000..684379d --- /dev/null +++ b/libs/locale/src/boost/locale/util/timezone.hpp @@ -0,0 +1,47 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_UTIL_TIMEZONE_HPP +#define BOOST_LOCALE_IMPL_UTIL_TIMEZONE_HPP +#include +#include +#include + +namespace boost { namespace locale { namespace util { + inline int parse_tz(const std::string& tz) + { + int gmtoff = 0; + std::string ltz; + for(unsigned i = 0; i < tz.size(); i++) { + if('a' <= tz[i] && tz[i] <= 'z') + ltz += tz[i] - 'a' + 'A'; + else if(tz[i] == ' ') + ; + else + ltz += tz[i]; + } + if(ltz.compare(0, 3, "GMT") != 0 && ltz.compare(0, 3, "UTC") != 0) + return 0; + if(ltz.size() <= 3) + return 0; + const char* begin = ltz.c_str() + 3; + char* end = 0; + int hours = strtol(begin, &end, 10); + if(end != begin) { + gmtoff += hours * 3600; + } + if(*end == ':') { + begin = end + 1; + int minutes = strtol(begin, &end, 10); + if(end != begin) + gmtoff += minutes * 60; + } + return gmtoff; + } + +}}} // namespace boost::locale::util + +#endif diff --git a/libs/locale/src/boost/locale/util/win_codepages.hpp b/libs/locale/src/boost/locale/util/win_codepages.hpp new file mode 100644 index 0000000..4c3df66 --- /dev/null +++ b/libs/locale/src/boost/locale/util/win_codepages.hpp @@ -0,0 +1,181 @@ +#ifndef BOOST_LOCALE_IMPL_WIN_CODEPAGES_HPP +#define BOOST_LOCALE_IMPL_WIN_CODEPAGES_HPP + +#include +#include + +namespace boost { namespace locale { namespace util { + + struct windows_encoding { + const char* name; + unsigned codepage; + unsigned was_tested; + }; + + inline bool operator<(const windows_encoding& l, const char* name) + { + return strcmp(l.name, name) < 0; + } + + static windows_encoding all_windows_encodings[] = { + {"asmo708", 708, 0}, + {"big5", 950, 0}, + {"cp1025", 21025, 0}, + {"cp1250", 1250, 0}, + {"cp1251", 1251, 0}, + {"cp1252", 1252, 0}, + {"cp1253", 1253, 0}, + {"cp1254", 1254, 0}, + {"cp1255", 1255, 0}, + {"cp1256", 1256, 0}, + {"cp1257", 1257, 0}, + {"cp866", 866, 0}, + {"cp874", 874, 0}, + {"cp875", 875, 0}, + {"cp932", 932, 0}, + {"cp936", 936, 0}, + {"csiso2022jp", 50221, 0}, + {"dos720", 720, 0}, + {"dos862", 862, 0}, + {"euccn", 51936, 0}, + {"eucjp", 20932, 0}, + {"euckr", 51949, 0}, + {"gb18030", 54936, 0}, + {"gb2312", 936, 0}, + {"gbk", 936, 0}, + {"hzgb2312", 52936, 0}, + {"ibm00858", 858, 0}, + {"ibm00924", 20924, 0}, + {"ibm01026", 1026, 0}, + {"ibm01047", 1047, 0}, + {"ibm01140", 1140, 0}, + {"ibm01141", 1141, 0}, + {"ibm01142", 1142, 0}, + {"ibm01143", 1143, 0}, + {"ibm01144", 1144, 0}, + {"ibm01145", 1145, 0}, + {"ibm01146", 1146, 0}, + {"ibm01147", 1147, 0}, + {"ibm01148", 1148, 0}, + {"ibm01149", 1149, 0}, + {"ibm037", 37, 0}, + {"ibm273", 20273, 0}, + {"ibm277", 20277, 0}, + {"ibm278", 20278, 0}, + {"ibm280", 20280, 0}, + {"ibm284", 20284, 0}, + {"ibm285", 20285, 0}, + {"ibm290", 20290, 0}, + {"ibm297", 20297, 0}, + {"ibm420", 20420, 0}, + {"ibm423", 20423, 0}, + {"ibm424", 20424, 0}, + {"ibm437", 437, 0}, + {"ibm500", 500, 0}, + {"ibm737", 737, 0}, + {"ibm775", 775, 0}, + {"ibm850", 850, 0}, + {"ibm852", 852, 0}, + {"ibm855", 855, 0}, + {"ibm857", 857, 0}, + {"ibm860", 860, 0}, + {"ibm861", 861, 0}, + {"ibm863", 863, 0}, + {"ibm864", 864, 0}, + {"ibm865", 865, 0}, + {"ibm869", 869, 0}, + {"ibm870", 870, 0}, + {"ibm871", 20871, 0}, + {"ibm880", 20880, 0}, + {"ibm905", 20905, 0}, + {"ibmthai", 20838, 0}, + {"iso2022jp", 50220, 0}, + {"iso2022jp", 50222, 0}, + {"iso2022kr", 50225, 0}, + {"iso88591", 28591, 0}, + {"iso885913", 28603, 0}, + {"iso885915", 28605, 0}, + {"iso88592", 28592, 0}, + {"iso88593", 28593, 0}, + {"iso88594", 28594, 0}, + {"iso88595", 28595, 0}, + {"iso88596", 28596, 0}, + {"iso88597", 28597, 0}, + {"iso88598", 28598, 0}, + {"iso88598i", 38598, 0}, + {"iso88599", 28599, 0}, + {"johab", 1361, 0}, + {"koi8r", 20866, 0}, + {"koi8u", 21866, 0}, + {"ksc56011987", 949, 0}, + {"latin1", 28591, 0}, + {"latin1", 1252, 0}, + {"macintosh", 10000, 0}, + {"ms936", 936, 0}, + {"shiftjis", 932, 0}, + {"sjis", 932, 0}, + {"unicodefffe", 1201, 0}, + {"usascii", 20127, 0}, + {"utf16", 1200, 0}, + {"utf32", 12000, 0}, + {"utf32be", 12001, 0}, + {"utf7", 65000, 0}, + {"utf8", 65001, 0}, + {"windows1250", 1250, 0}, + {"windows1251", 1251, 0}, + {"windows1252", 1252, 0}, + {"windows1253", 1253, 0}, + {"windows1254", 1254, 0}, + {"windows1255", 1255, 0}, + {"windows1256", 1256, 0}, + {"windows1257", 1257, 0}, + {"windows1258", 1258, 0}, + {"windows874", 874, 0}, + {"windows932", 932, 0}, + {"windows936", 936, 0}, + {"xchinesecns", 20000, 0}, + {"xchineseeten", 20002, 0}, + {"xcp20001", 20001, 0}, + {"xcp20003", 20003, 0}, + {"xcp20004", 20004, 0}, + {"xcp20005", 20005, 0}, + {"xcp20261", 20261, 0}, + {"xcp20269", 20269, 0}, + {"xcp20936", 20936, 0}, + {"xcp20949", 20949, 0}, + {"xcp50227", 50227, 0}, + {"xebcdickoreanextended", 20833, 0}, + {"xeuropa", 29001, 0}, + {"xia5", 20105, 0}, + {"xia5german", 20106, 0}, + {"xia5norwegian", 20108, 0}, + {"xia5swedish", 20107, 0}, + {"xisciias", 57006, 0}, + {"xisciibe", 57003, 0}, + {"xisciide", 57002, 0}, + {"xisciigu", 57010, 0}, + {"xisciika", 57008, 0}, + {"xisciima", 57009, 0}, + {"xisciior", 57007, 0}, + {"xisciipa", 57011, 0}, + {"xisciita", 57004, 0}, + {"xisciite", 57005, 0}, + {"xmacarabic", 10004, 0}, + {"xmacce", 10029, 0}, + {"xmacchinesesimp", 10008, 0}, + {"xmacchinesetrad", 10002, 0}, + {"xmaccroatian", 10082, 0}, + {"xmaccyrillic", 10007, 0}, + {"xmacgreek", 10006, 0}, + {"xmachebrew", 10005, 0}, + {"xmacicelandic", 10079, 0}, + {"xmacjapanese", 10001, 0}, + {"xmackorean", 10003, 0}, + {"xmacromanian", 10010, 0}, + {"xmacthai", 10021, 0}, + {"xmacturkish", 10081, 0}, + {"xmacukrainian", 10017, 0}, + }; +}}} // namespace boost::locale::util + +#endif \ No newline at end of file diff --git a/libs/locale/src/boost/locale/win32/all_generator.hpp b/libs/locale/src/boost/locale/win32/all_generator.hpp new file mode 100644 index 0000000..ad1984b --- /dev/null +++ b/libs/locale/src/boost/locale/win32/all_generator.hpp @@ -0,0 +1,29 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_WIN32_ALL_GENERATOR_HPP +#define BOOST_LOCALE_IMPL_WIN32_ALL_GENERATOR_HPP + +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + class winlocale; + + std::locale create_convert(const std::locale& in, const winlocale& lc, char_facet_t type); + + std::locale create_collate(const std::locale& in, const winlocale& lc, char_facet_t type); + + std::locale create_formatting(const std::locale& in, const winlocale& lc, char_facet_t type); + + std::locale create_parsing(const std::locale& in, const winlocale& lc, char_facet_t type); + + std::locale create_codecvt(const std::locale& in, char_facet_t type); + +}}} // namespace boost::locale::impl_win + +#endif diff --git a/libs/locale/src/boost/locale/win32/api.hpp b/libs/locale/src/boost/locale/win32/api.hpp new file mode 100644 index 0000000..a02d347 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/api.hpp @@ -0,0 +1,324 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_WIN32_API_HPP +#define BOOST_LOCALE_IMPL_WIN32_API_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "boost/locale/win32/lcid.hpp" + +#ifndef NOMINMAX +# define NOMINMAX +#endif +#ifndef UNICODE +# define UNICODE +#endif +#include + +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + struct numeric_info { + std::wstring thousands_sep; + std::wstring decimal_point; + std::string grouping; + }; + + inline DWORD collation_level_to_flag(collate_level level) + { + switch(level) { + case collate_level::primary: return NORM_IGNORESYMBOLS | NORM_IGNORECASE | NORM_IGNORENONSPACE; + case collate_level::secondary: return NORM_IGNORESYMBOLS | NORM_IGNORECASE; + case collate_level::tertiary: return NORM_IGNORESYMBOLS; + case collate_level::quaternary: + case collate_level::identical: return 0; + } + return 0; + } + + class winlocale { + public: + winlocale() : lcid(0) {} + + winlocale(const std::string& name) { lcid = locale_to_lcid(name); } + + unsigned lcid; + + bool is_c() const { return lcid == 0; } + }; + + //////////////////////////////////////////////////////////////////////// + /// + /// Number Format + /// + //////////////////////////////////////////////////////////////////////// + + inline numeric_info wcsnumformat_l(const winlocale& l) + { + numeric_info res; + res.decimal_point = L'.'; + unsigned lcid = l.lcid; + + if(lcid == 0) + return res; + + // limits according to MSDN + constexpr int th_size = 4; + constexpr int de_size = 4; + constexpr int gr_size = 10; + + wchar_t th[th_size] = {0}; + wchar_t de[de_size] = {0}; + wchar_t gr[gr_size] = {0}; + + if(GetLocaleInfoW(lcid, LOCALE_STHOUSAND, th, th_size) == 0 + || GetLocaleInfoW(lcid, LOCALE_SDECIMAL, de, de_size) == 0 + || GetLocaleInfoW(lcid, LOCALE_SGROUPING, gr, gr_size) == 0) + { + return res; + } + res.decimal_point = de; + res.thousands_sep = th; + bool inf_group = false; + for(unsigned i = 0; gr[i]; i++) { + if(gr[i] == L';') + continue; + if(L'1' <= gr[i] && gr[i] <= L'9') { + res.grouping += char(gr[i] - L'0'); + } else if(gr[i] == L'0') + inf_group = true; + } + if(!inf_group) { + BOOST_LOCALE_START_CONST_CONDITION + if(std::numeric_limits::is_signed) { + res.grouping += std::numeric_limits::min(); + } else { + res.grouping += std::numeric_limits::max(); + } + BOOST_LOCALE_END_CONST_CONDITION + } + return res; + } + + inline std::wstring win_map_string_l(unsigned flags, const wchar_t* begin, const wchar_t* end, const winlocale& l) + { + std::wstring res; + if(end - begin > std::numeric_limits::max()) + throw std::length_error("String to long for int type"); + int len = LCMapStringW(l.lcid, flags, begin, static_cast(end - begin), 0, 0); + if(len == 0) + return res; + if(len == std::numeric_limits::max()) + throw std::length_error("String to long for int type"); + std::vector buf(len + 1); + int l2 = + LCMapStringW(l.lcid, flags, begin, static_cast(end - begin), &buf.front(), static_cast(buf.size())); + res.assign(&buf.front(), l2); + return res; + } + + //////////////////////////////////////////////////////////////////////// + /// + /// Collation + /// + //////////////////////////////////////////////////////////////////////// + + inline int wcscoll_l(collate_level level, + const wchar_t* lb, + const wchar_t* le, + const wchar_t* rb, + const wchar_t* re, + const winlocale& l) + { + if(le - lb > std::numeric_limits::max() || re - rb > std::numeric_limits::max()) + throw std::length_error("String to long for int type"); + const int result = CompareStringW(l.lcid, + collation_level_to_flag(level), + lb, + static_cast(le - lb), + rb, + static_cast(re - rb)); + return result - 2; // Subtract 2 to get the meaning of <0, ==0, and >0 + } + + //////////////////////////////////////////////////////////////////////// + /// + /// Money Format + /// + //////////////////////////////////////////////////////////////////////// + + inline std::wstring wcsfmon_l(double value, const winlocale& l) + { + std::wostringstream ss; + ss.imbue(std::locale::classic()); + + ss << std::setprecision(std::numeric_limits::digits10 + 1) << value; + std::wstring sval = ss.str(); + int len = GetCurrencyFormatW(l.lcid, 0, sval.c_str(), 0, 0, 0); + std::vector buf(len + 1); + GetCurrencyFormatW(l.lcid, 0, sval.c_str(), 0, &buf.front(), len); + return &buf.front(); + } + + //////////////////////////////////////////////////////////////////////// + /// + /// Time Format + /// + //////////////////////////////////////////////////////////////////////// + + inline std::wstring wcs_format_date_l(const wchar_t* format, SYSTEMTIME const* tm, const winlocale& l) + { + int len = GetDateFormatW(l.lcid, 0, tm, format, 0, 0); + std::vector buf(len + 1); + GetDateFormatW(l.lcid, 0, tm, format, &buf.front(), len); + return &buf.front(); + } + + inline std::wstring wcs_format_time_l(const wchar_t* format, SYSTEMTIME const* tm, const winlocale& l) + { + int len = GetTimeFormatW(l.lcid, 0, tm, format, 0, 0); + std::vector buf(len + 1); + GetTimeFormatW(l.lcid, 0, tm, format, &buf.front(), len); + return &buf.front(); + } + + inline std::wstring wcsfold(const wchar_t* begin, const wchar_t* end) + { + winlocale l; + l.lcid = 0x0409; // en-US + return win_map_string_l(LCMAP_LOWERCASE, begin, end, l); + } + + inline std::wstring wcsnormalize(norm_type norm, const wchar_t* begin, const wchar_t* end) + { + // We use FoldString, under Vista it actually does normalization; + // under XP and below it does something similar, half job, better then nothing + unsigned flags = MAP_PRECOMPOSED; + switch(norm) { + case norm_nfd: flags = MAP_COMPOSITE; break; + case norm_nfc: flags = MAP_PRECOMPOSED; break; + case norm_nfkd: flags = MAP_FOLDCZONE; break; + case norm_nfkc: flags = MAP_FOLDCZONE | MAP_COMPOSITE; break; + } + + if(end - begin > std::numeric_limits::max()) + throw std::length_error("String to long for int type"); + int len = FoldStringW(flags, begin, static_cast(end - begin), 0, 0); + if(len == 0) + return std::wstring(); + if(len == std::numeric_limits::max()) + throw std::length_error("String to long for int type"); + std::vector v(len + 1); + len = FoldStringW(flags, begin, static_cast(end - begin), &v.front(), len + 1); + return std::wstring(&v.front(), len); + } + + inline std::wstring wcsxfrm_l(collate_level level, const wchar_t* begin, const wchar_t* end, const winlocale& l) + { + int flag = LCMAP_SORTKEY | collation_level_to_flag(level); + + return win_map_string_l(flag, begin, end, l); + } + + inline std::wstring towupper_l(const wchar_t* begin, const wchar_t* end, const winlocale& l) + { + return win_map_string_l(LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING, begin, end, l); + } + + inline std::wstring towlower_l(const wchar_t* begin, const wchar_t* end, const winlocale& l) + { + return win_map_string_l(LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING, begin, end, l); + } + + inline std::wstring wcsftime_l(char c, const std::tm* tm, const winlocale& l) + { + SYSTEMTIME wtm = SYSTEMTIME(); + wtm.wYear = static_cast(tm->tm_year + 1900); + wtm.wMonth = static_cast(tm->tm_mon + 1); + wtm.wDayOfWeek = static_cast(tm->tm_wday); + wtm.wDay = static_cast(tm->tm_mday); + wtm.wHour = static_cast(tm->tm_hour); + wtm.wMinute = static_cast(tm->tm_min); + wtm.wSecond = static_cast(tm->tm_sec); + switch(c) { + case 'a': // Abbr Weekday + return wcs_format_date_l(L"ddd", &wtm, l); + case 'A': // Full Weekday + return wcs_format_date_l(L"dddd", &wtm, l); + case 'b': // Abbr Month + return wcs_format_date_l(L"MMM", &wtm, l); + case 'B': // Full Month + return wcs_format_date_l(L"MMMM", &wtm, l); + case 'c': // DateTile Full + return wcs_format_date_l(0, &wtm, l) + L" " + wcs_format_time_l(0, &wtm, l); + // not supported by WIN ;( + // case 'C': // Century -> 1980 -> 19 + // retur + case 'd': // Day of Month [01,31] + return wcs_format_date_l(L"dd", &wtm, l); + case 'D': // %m/%d/%y + return wcs_format_date_l(L"MM/dd/yy", &wtm, l); + case 'e': // Day of Month [1,31] + return wcs_format_date_l(L"d", &wtm, l); + case 'h': // == b + return wcs_format_date_l(L"MMM", &wtm, l); + case 'H': // 24 clock hour 00,23 + return wcs_format_time_l(L"HH", &wtm, l); + case 'I': // 12 clock hour 01,12 + return wcs_format_time_l(L"hh", &wtm, l); + /* + case 'j': // day of year 001,366 + return "D";*/ + case 'm': // month as [01,12] + return wcs_format_date_l(L"MM", &wtm, l); + case 'M': // minute [00,59] + return wcs_format_time_l(L"mm", &wtm, l); + case 'n': // \n + return L"\n"; + case 'p': // am-pm + return wcs_format_time_l(L"tt", &wtm, l); + case 'r': // time with AM/PM %I:%M:%S %p + return wcs_format_time_l(L"hh:mm:ss tt", &wtm, l); + case 'R': // %H:%M + return wcs_format_time_l(L"HH:mm", &wtm, l); + case 'S': // second [00,61] + return wcs_format_time_l(L"ss", &wtm, l); + case 't': // \t + return L"\t"; + case 'T': // %H:%M:%S + return wcs_format_time_l(L"HH:mm:ss", &wtm, l); + /* case 'u': // weekday 1,7 1=Monday + case 'U': // week number of year [00,53] Sunday first + case 'V': // week number of year [01,53] Monday first + case 'w': // weekday 0,7 0=Sunday + case 'W': // week number of year [00,53] Monday first, */ + case 'x': // Date + return wcs_format_date_l(0, &wtm, l); + case 'X': // Time + return wcs_format_time_l(0, &wtm, l); + case 'y': // Year [00-99] + return wcs_format_date_l(L"yy", &wtm, l); + case 'Y': // Year 1998 + return wcs_format_date_l(L"yyyy", &wtm, l); + case '%': // % + return L"%"; + default: return L""; + } + } + +}}} // namespace boost::locale::impl_win + +// boostinspect:nominmax +#endif diff --git a/libs/locale/src/boost/locale/win32/collate.cpp b/libs/locale/src/boost/locale/win32/collate.cpp new file mode 100644 index 0000000..bd78c21 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/collate.cpp @@ -0,0 +1,121 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include "boost/locale/shared/mo_hash.hpp" +#include "boost/locale/win32/api.hpp" +#include +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + class utf8_collator : public collator { + public: + utf8_collator(winlocale lc, size_t refs = 0) : collator(refs), lc_(lc) {} + int + do_compare(collate_level level, const char* lb, const char* le, const char* rb, const char* re) const override + { + std::wstring l = conv::to_utf(lb, le, "UTF-8"); + std::wstring r = conv::to_utf(rb, re, "UTF-8"); + return wcscoll_l(level, l.c_str(), l.c_str() + l.size(), r.c_str(), r.c_str() + r.size(), lc_); + } + long do_hash(collate_level level, const char* b, const char* e) const override + { + std::string key = do_transform(level, b, e); + return gnu_gettext::pj_winberger_hash_function(key.c_str(), key.c_str() + key.size()); + } + std::string do_transform(collate_level level, const char* b, const char* e) const override + { + std::wstring tmp = conv::to_utf(b, e, "UTF-8"); + std::wstring wkey = wcsxfrm_l(level, tmp.c_str(), tmp.c_str() + tmp.size(), lc_); + std::string key; + BOOST_LOCALE_START_CONST_CONDITION + if(sizeof(wchar_t) == 2) + key.reserve(wkey.size() * 2); + else + key.reserve(wkey.size() * 3); + for(unsigned i = 0; i < wkey.size(); i++) { + if(sizeof(wchar_t) == 2) { + uint16_t tv = static_cast(wkey[i]); + key += char(tv >> 8); + key += char(tv & 0xFF); + } else { // 4 + uint32_t tv = static_cast(wkey[i]); + // 21 bit + key += char((tv >> 16) & 0xFF); + key += char((tv >> 8) & 0xFF); + key += char(tv & 0xFF); + } + } + BOOST_LOCALE_END_CONST_CONDITION + return key; + } + + private: + winlocale lc_; + }; + + class utf16_collator : public collator { + public: + typedef std::collate wfacet; + utf16_collator(winlocale lc, size_t refs = 0) : collator(refs), lc_(lc) {} + int do_compare(collate_level level, + const wchar_t* lb, + const wchar_t* le, + const wchar_t* rb, + const wchar_t* re) const override + { + return wcscoll_l(level, lb, le, rb, re, lc_); + } + long do_hash(collate_level level, const wchar_t* b, const wchar_t* e) const override + { + std::wstring key = do_transform(level, b, e); + const char* begin = reinterpret_cast(key.c_str()); + const char* end = begin + key.size() * sizeof(wchar_t); + return gnu_gettext::pj_winberger_hash_function(begin, end); + } + std::wstring do_transform(collate_level level, const wchar_t* b, const wchar_t* e) const override + { + return wcsxfrm_l(level, b, e, lc_); + } + + private: + winlocale lc_; + }; + + std::locale create_collate(const std::locale& in, const winlocale& lc, char_facet_t type) + { + if(lc.is_c()) { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new std::collate_byname("C")); + case char_facet_t::wchar_f: return std::locale(in, new std::collate_byname("C")); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return std::locale(in, new collate_byname("C")); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return std::locale(in, new collate_byname("C")); +#endif + } + } else { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new utf8_collator(lc)); + case char_facet_t::wchar_f: return std::locale(in, new utf16_collator(lc)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: break; +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: break; +#endif + } + } + return in; + } + +}}} // namespace boost::locale::impl_win diff --git a/libs/locale/src/boost/locale/win32/converter.cpp b/libs/locale/src/boost/locale/win32/converter.cpp new file mode 100644 index 0000000..6911ab5 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/converter.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include "boost/locale/win32/all_generator.hpp" +#include "boost/locale/win32/api.hpp" +#include +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + class utf16_converter : public converter { + public: + utf16_converter(const winlocale& lc, size_t refs = 0) : converter(refs), lc_(lc) {} + std::wstring convert(converter_base::conversion_type how, + const wchar_t* begin, + const wchar_t* end, + int flags = 0) const override + { + switch(how) { + case converter_base::upper_case: return towupper_l(begin, end, lc_); + case converter_base::lower_case: return towlower_l(begin, end, lc_); + case converter_base::case_folding: return wcsfold(begin, end); + case converter_base::normalization: return wcsnormalize(static_cast(flags), begin, end); + case converter_base::title_case: break; + } + return std::wstring(begin, end - begin); + } + + private: + winlocale lc_; + }; + + class utf8_converter : public converter { + public: + utf8_converter(const winlocale& lc, size_t refs = 0) : converter(refs), lc_(lc) {} + std::string + convert(converter_base::conversion_type how, const char* begin, const char* end, int flags = 0) const override + { + std::wstring tmp = conv::to_utf(begin, end, "UTF-8"); + const wchar_t* wb = tmp.c_str(); + const wchar_t* we = wb + tmp.size(); + + std::wstring res; + + switch(how) { + case upper_case: res = towupper_l(wb, we, lc_); break; + case lower_case: res = towlower_l(wb, we, lc_); break; + case case_folding: res = wcsfold(wb, we); break; + case normalization: res = wcsnormalize(static_cast(flags), wb, we); break; + case title_case: break; + } + return conv::from_utf(res, "UTF-8"); + } + + private: + winlocale lc_; + }; + + std::locale create_convert(const std::locale& in, const winlocale& lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return std::locale(in, new utf8_converter(lc)); + case char_facet_t::wchar_f: return std::locale(in, new utf16_converter(lc)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: break; +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: break; +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_win diff --git a/libs/locale/src/boost/locale/win32/lcid.cpp b/libs/locale/src/boost/locale/win32/lcid.cpp new file mode 100644 index 0000000..f27cd09 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/lcid.cpp @@ -0,0 +1,115 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef NOMINMAX +# define NOMINMAX +#endif + +#include "boost/locale/win32/lcid.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + typedef std::map table_type; + + static table_type* volatile table = 0; + + boost::mutex& lcid_table_mutex() + { + static boost::mutex m; + return m; + } + + table_type& real_lcid_table() + { + static table_type real_table; + return real_table; + } + + BOOL CALLBACK proc(char* s) + { + table_type& tbl = real_lcid_table(); + try { + std::istringstream ss; + ss.str(s); + ss >> std::hex; + + unsigned lcid; + ss >> lcid; + if(ss.fail() || !ss.eof()) { + return FALSE; + } + + char iso_639_lang[16]; + char iso_3166_country[16]; + if(GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, iso_639_lang, sizeof(iso_639_lang)) == 0) + return FALSE; + std::string lc_name = iso_639_lang; + if(GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, iso_3166_country, sizeof(iso_3166_country)) != 0) { + lc_name += "_"; + lc_name += iso_3166_country; + } + table_type::iterator p = tbl.find(lc_name); + if(p != tbl.end()) { + if(p->second > lcid) + p->second = lcid; + } else { + tbl[lc_name] = lcid; + } + } catch(...) { + tbl.clear(); + return FALSE; + } + return TRUE; + } + + const table_type& get_ready_lcid_table() + { + if(table) + return *table; + else { + boost::unique_lock lock(lcid_table_mutex()); + if(table) + return *table; + EnumSystemLocalesA(proc, LCID_INSTALLED); + table = &real_lcid_table(); + return *table; + } + } + + unsigned locale_to_lcid(const std::string& locale_name) + { + if(locale_name.empty()) { + return LOCALE_USER_DEFAULT; + } + boost::locale::util::locale_data d; + d.parse(locale_name); + std::string id = d.language(); + + if(!d.country().empty()) { + id += "_" + d.country(); + } + if(!d.variant().empty()) { + id += "@" + d.variant(); + } + + const table_type& tbl = get_ready_lcid_table(); + table_type::const_iterator p = tbl.find(id); + + unsigned lcid = 0; + if(p != tbl.end()) + lcid = p->second; + return lcid; + } + +}}} // namespace boost::locale::impl_win diff --git a/libs/locale/src/boost/locale/win32/lcid.hpp b/libs/locale/src/boost/locale/win32/lcid.hpp new file mode 100644 index 0000000..19a801d --- /dev/null +++ b/libs/locale/src/boost/locale/win32/lcid.hpp @@ -0,0 +1,19 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_WIN32_LCID_HPP +#define BOOST_LOCALE_IMPL_WIN32_LCID_HPP + +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + BOOST_LOCALE_DECL unsigned locale_to_lcid(const std::string& locale_name); + +}}} // namespace boost::locale::impl_win + +#endif diff --git a/libs/locale/src/boost/locale/win32/numeric.cpp b/libs/locale/src/boost/locale/win32/numeric.cpp new file mode 100644 index 0000000..2654be7 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/numeric.cpp @@ -0,0 +1,210 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/util/numeric.hpp" +#include +#include +#include +#include "boost/locale/win32/all_generator.hpp" +#include "boost/locale/win32/api.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace locale { namespace impl_win { + namespace { + + std::ostreambuf_iterator write_it(std::ostreambuf_iterator out, const std::wstring& s) + { + for(size_t i = 0; i < s.size(); i++) + *out++ = s[i]; + return out; + } + + std::ostreambuf_iterator write_it(std::ostreambuf_iterator out, const std::wstring& s) + { + std::string tmp = conv::from_utf(s, "UTF-8"); + for(size_t i = 0; i < tmp.size(); i++) + *out++ = tmp[i]; + return out; + } + } // namespace + + template + class num_format : public util::base_num_format { + public: + typedef typename std::num_put::iter_type iter_type; + typedef std::basic_string string_type; + typedef CharType char_type; + + num_format(const winlocale& lc, size_t refs = 0) : util::base_num_format(refs), lc_(lc) {} + + private: + iter_type do_format_currency(bool /*intl*/, + iter_type out, + std::ios_base& ios, + char_type fill, + long double val) const override + { + if(lc_.is_c()) { + std::locale loc = ios.getloc(); + int digits = std::use_facet>(loc).frac_digits(); + while(digits > 0) { + val *= 10; + digits--; + } + std::ios_base::fmtflags f = ios.flags(); + ios.flags(f | std::ios_base::showbase); + out = std::use_facet>(loc).put(out, false, ios, fill, val); + ios.flags(f); + return out; + } else { + std::wstring cur = wcsfmon_l(static_cast(val), lc_); + return write_it(out, cur); + } + } + + private: + winlocale lc_; + + }; /// num_format + + template + class time_put_win : public std::time_put { + public: + time_put_win(const winlocale& lc, size_t refs = 0) : std::time_put(refs), lc_(lc) {} + + typedef typename std::time_put::iter_type iter_type; + typedef CharType char_type; + typedef std::basic_string string_type; + + iter_type do_put(iter_type out, + std::ios_base& /*ios*/, + CharType /*fill*/, + const std::tm* tm, + char format, + char /*modifier*/) const override + { + return write_it(out, wcsftime_l(format, tm, lc_)); + } + + private: + winlocale lc_; + }; + + template + class num_punct_win : public std::numpunct { + public: + typedef std::basic_string string_type; + num_punct_win(const winlocale& lc, size_t refs = 0) : std::numpunct(refs) + { + numeric_info np = wcsnumformat_l(lc); + + BOOST_LOCALE_START_CONST_CONDITION + if(sizeof(CharType) == 1 && np.thousands_sep == L"\xA0") + np.thousands_sep = L" "; + BOOST_LOCALE_END_CONST_CONDITION + + to_str(np.thousands_sep, thousands_sep_); + to_str(np.decimal_point, decimal_point_); + grouping_ = np.grouping; + if(thousands_sep_.size() > 1) + grouping_ = std::string(); + if(decimal_point_.size() > 1) + decimal_point_ = CharType('.'); + } + + void to_str(std::wstring& s1, std::wstring& s2) { s2.swap(s1); } + + void to_str(std::wstring& s1, std::string& s2) { s2 = conv::from_utf(s1, "UTF-8"); } + CharType do_decimal_point() const override { return *decimal_point_.c_str(); } + CharType do_thousands_sep() const override { return *thousands_sep_.c_str(); } + std::string do_grouping() const override { return grouping_; } + string_type do_truename() const override + { + static const char t[] = "true"; + return string_type(t, t + sizeof(t) - 1); + } + string_type do_falsename() const override + { + static const char t[] = "false"; + return string_type(t, t + sizeof(t) - 1); + } + + private: + string_type decimal_point_; + string_type thousands_sep_; + std::string grouping_; + }; + + template + std::locale create_formatting_impl(const std::locale& in, const winlocale& lc) + { + if(lc.is_c()) { + std::locale tmp(in, new std::numpunct_byname("C")); + tmp = std::locale(tmp, new std::time_put_byname("C")); + tmp = std::locale(tmp, new num_format(lc)); + return tmp; + } else { + std::locale tmp(in, new num_punct_win(lc)); + tmp = std::locale(tmp, new time_put_win(lc)); + tmp = std::locale(tmp, new num_format(lc)); + return tmp; + } + } + + template + std::locale create_parsing_impl(const std::locale& in, const winlocale& lc) + { + std::numpunct* np = 0; + if(lc.is_c()) + np = new std::numpunct_byname("C"); + else + np = new num_punct_win(lc); + std::locale tmp(in, np); + tmp = std::locale(tmp, new util::base_num_parse()); + return tmp; + } + + std::locale create_formatting(const std::locale& in, const winlocale& lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return create_formatting_impl(in, lc); + case char_facet_t::wchar_f: return create_formatting_impl(in, lc); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return create_formatting_impl(in, lc); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return create_formatting_impl(in, lc); +#endif + } + return in; + } + + std::locale create_parsing(const std::locale& in, const winlocale& lc, char_facet_t type) + { + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: return create_parsing_impl(in, lc); + case char_facet_t::wchar_f: return create_parsing_impl(in, lc); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: return create_parsing_impl(in, lc); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: return create_parsing_impl(in, lc); +#endif + } + return in; + } + +}}} // namespace boost::locale::impl_win diff --git a/libs/locale/src/boost/locale/win32/win_backend.cpp b/libs/locale/src/boost/locale/win32/win_backend.cpp new file mode 100644 index 0000000..c1a8107 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/win_backend.cpp @@ -0,0 +1,134 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "boost/locale/win32/win_backend.hpp" +#include +#include +#include +#include +#include +#include "boost/locale/util/gregorian.hpp" +#include "boost/locale/win32/all_generator.hpp" +#include "boost/locale/win32/api.hpp" +#include +#include +#include + +namespace boost { namespace locale { namespace impl_win { + + class winapi_localization_backend : public localization_backend { + public: + winapi_localization_backend() : invalid_(true) {} + winapi_localization_backend(const winapi_localization_backend& other) : + localization_backend(), paths_(other.paths_), domains_(other.domains_), locale_id_(other.locale_id_), + invalid_(true) + {} + winapi_localization_backend* clone() const override { return new winapi_localization_backend(*this); } + + void set_option(const std::string& name, const std::string& value) override + { + invalid_ = true; + if(name == "locale") + locale_id_ = value; + else if(name == "message_path") + paths_.push_back(value); + else if(name == "message_application") + domains_.push_back(value); + } + void clear_options() override + { + invalid_ = true; + locale_id_.clear(); + paths_.clear(); + domains_.clear(); + } + + void prepare_data() + { + if(!invalid_) + return; + invalid_ = false; + if(locale_id_.empty()) { + real_id_ = util::get_system_locale(true); // always UTF-8 + lc_ = winlocale(real_id_); + } else { + lc_ = winlocale(locale_id_); + real_id_ = locale_id_; + } + util::locale_data d; + d.parse(real_id_); + if(!d.is_utf8()) { + lc_ = winlocale(); + // Make it C as non-UTF8 locales are not supported + } + } + + std::locale install(const std::locale& base, category_t category, char_facet_t type) override + { + prepare_data(); + + switch(category) { + case category_t::convert: return create_convert(base, lc_, type); + case category_t::collation: return create_collate(base, lc_, type); + case category_t::formatting: return create_formatting(base, lc_, type); + case category_t::parsing: return create_parsing(base, lc_, type); + case category_t::calendar: { + util::locale_data inf; + inf.parse(real_id_); + return util::install_gregorian_calendar(base, inf.country()); + } + case category_t::message: { + gnu_gettext::messages_info minf; + std::locale tmp = util::create_info(std::locale::classic(), real_id_); + const boost::locale::info& inf = std::use_facet(tmp); + minf.language = inf.language(); + minf.country = inf.country(); + minf.variant = inf.variant(); + minf.encoding = inf.encoding(); + std::copy(domains_.begin(), + domains_.end(), + std::back_inserter(minf.domains)); + minf.paths = paths_; + switch(type) { + case char_facet_t::nochar: break; + case char_facet_t::char_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); + case char_facet_t::wchar_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + case char_facet_t::char16_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + case char_facet_t::char32_f: + return std::locale(base, gnu_gettext::create_messages_facet(minf)); +#endif + } + return base; + } + case category_t::information: return util::create_info(base, real_id_); + case category_t::codepage: return util::create_utf8_codecvt(base, type); + case category_t::boundary: break; // Not implemented + } + return base; + } + + private: + std::vector paths_; + std::vector domains_; + std::string locale_id_; + std::string real_id_; + + bool invalid_; + winlocale lc_; + }; + + localization_backend* create_localization_backend() + { + return new winapi_localization_backend(); + } + +}}} // namespace boost::locale::impl_win diff --git a/libs/locale/src/boost/locale/win32/win_backend.hpp b/libs/locale/src/boost/locale/win32/win_backend.hpp new file mode 100644 index 0000000..57fa5c2 --- /dev/null +++ b/libs/locale/src/boost/locale/win32/win_backend.hpp @@ -0,0 +1,15 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_WIN32_LOCALIZATION_BACKEND_HPP +#define BOOST_LOCALE_IMPL_WIN32_LOCALIZATION_BACKEND_HPP +namespace boost { namespace locale { + class localization_backend; + namespace impl_win { + localization_backend* create_localization_backend(); + } // namespace impl_win +}} // namespace boost::locale +#endif diff --git a/libs/thread/build/Jamfile.v2 b/libs/thread/build/Jamfile.v2 new file mode 100644 index 0000000..1a88d01 --- /dev/null +++ b/libs/thread/build/Jamfile.v2 @@ -0,0 +1,319 @@ +# $Id$ +# Copyright 2006-2007 Roland Schwarz. +# Copyright 2007 Anthony Williams +# Copyright 2011-2012 Vicente J.Botet Escriba. +# 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 boost threading library can be built on top of different API's +# Currently this is the win32 API and the pthreads API. +# Pthread is native on unix variants. +# To get pthread on windows you need the pthread win32 library +# http://sourceware.org/pthreads-win32 which is available under LGPL. +# +# You need to provide the include path and lib path in the variables +# PTW32_INCLUDE and PTW32_LIB respectively. You can specify these +# paths in site-config.jam, user-config.jam or in the environment. +# A new feature is provided to request a specific API: +# win32 and pthread. +# +# The naming of the resulting libraries is mostly the same for the +# variant native to the build platform, i.e. +# boost_thread and the boost specific tagging. +# For the library variant that is not native on the build platform +# an additional tag is applied: +# boost_thread_pthread for the pthread variant on windows, and +# boost_thread_win32 for the win32 variant (likely when built on cygwin). +# +# To request the pthread variant on windows, from boost root you would +# say e.g: +# bjam msvc-8.0 --with-thread install threadapi=pthread +######################################################################### + +import os ; +import indirect ; +import path ; +import configure ; +import threadapi-feature ; + +exe has_atomic_flag_lockfree : ../build/has_atomic_flag_lockfree_test.cpp ; + +project boost/thread + : source-location ../src + : requirements multi + #static:BOOST_THREAD_STATIC_LINK=1 + #shared:BOOST_THREAD_DYN_LINK=1 + static:BOOST_THREAD_BUILD_LIB=1 + shared:BOOST_THREAD_BUILD_DLL=1 + -@$(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag + @$(__name__).tag + gcc:-Wno-long-long + #BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED + #BOOST_SYSTEM_NO_DEPRECATED + #BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + + #-pedantic -ansi -std=gnu++0x -Wextra -fpermissive + all + gcc:-Wextra + gcc:-pedantic + gcc:-Wno-long-long + #gcc:-ansi + #gcc:-fpermissive + gcc-4:-Wno-variadic-macros + gcc-5:-Wno-variadic-macros + #gcc:-Wunused-local-typedefs + gcc:-Wunused-function + gcc:-Wno-unused-parameter + + darwin:-Wextra + darwin:-pedantic + #darwin:-ansi + darwin:-fpermissive + darwin:-Wno-long-long + #darwin:-Wno-variadic-macros + darwin-4:-Wno-variadic-macros + darwin-5:-Wno-variadic-macros + #darwin:-Wunused-local-typedefs + darwin:-Wunused-function + darwin:-Wno-unused-parameter + + #pathscale:-Wextra + pathscale:-Wno-long-long + pathscale:-pedantic + + clang:on + clang:-Wextra + #clang:-ansi + #clang:-fpermissive + clang:-Wno-long-long + clang:-Wunused-function + clang:-Wno-variadic-macros + clang:-Wno-unused-parameter + + #gcc-mingw-4.4.0:-fdiagnostics-show-option + #gcc-mingw-4.5.0:-fdiagnostics-show-option + #gcc-mingw-4.6.0:-fdiagnostics-show-option + #gcc-mingw-4.6.3:-fdiagnostics-show-option + #gcc-mingw-4.7.0:-fdiagnostics-show-option + #gcc-mingw-4.8.0:-fdiagnostics-show-option + #gcc:-Wno-missing-field-initializers + + darwin-4.6.2:-Wno-delete-non-virtual-dtor + darwin-4.7.0:-Wno-delete-non-virtual-dtor + + #clang-2.8:-Wno-delete-non-virtual-dtor + #clang-2.8:-Wno-unused-function + #clang-2.9:-Wno-delete-non-virtual-dtor + #clang-2.9:-Wno-unused-function + clang-3.0:-Wno-delete-non-virtual-dtor + #clang-3.0:-Wno-unused-function + #clang-3.0:-Wno-unused-variable + +# Note: Some of the remarks from the Intel compiler are disabled +# remark #193: zero used for undefined preprocessing identifier "XXX" +# remark #304: access control not specified ("public" by default) +# remark #593: variable "XXX" was set but never used +# remark #1418: external function definition with no prior declaration +# remark #2415: variable "XXX" of static storage duration was declared but never referenced + + intel:-wd193,304,383,444 + intel:-wd593,981 + intel:-wd1418 + intel:-wd2415 + + msvc:/wd4100 + msvc:/wd4512 + msvc:/wd6246 + + windows:WIN32_LEAN_AND_MEAN + windows:BOOST_USE_WINDOWS_H + + # : default-build multi + : usage-requirements # pass these requirement to dependents (i.e. users) + #static:BOOST_THREAD_STATIC_LINK=1 + #shared:BOOST_THREAD_DYN_LINK=1 + static:BOOST_THREAD_BUILD_LIB=1 + shared:BOOST_THREAD_BUILD_DLL=1 + #BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED + #BOOST_SYSTEM_NO_DEPRECATED + #BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS + ; + +rule tag ( name : type ? : property-set ) +{ + local result = $(name) ; + + if $(type) in STATIC_LIB SHARED_LIB IMPORT_LIB + { + local api = [ $(property-set).get ] ; + + # non native api gets additional tag + if $(api) != [ threadapi-feature.get-default $(property-set) ] { + result = $(result)_$(api) ; + } + } + + # forward to the boost tagging rule + return [ indirect.call $(BOOST_JAMROOT_MODULE)%$(BOOST_JAMROOT_MODULE).tag + $(result) : $(type) : $(property-set) ] ; +} + +rule win32_pthread_paths ( properties * ) +{ + local result ; + local PTW32_INCLUDE ; + local PTW32_LIB ; + PTW32_INCLUDE = [ modules.peek : PTW32_INCLUDE ] ; + PTW32_LIB = [ modules.peek : PTW32_LIB ] ; + PTW32_INCLUDE ?= [ modules.peek user-config : PTW32_INCLUDE ] ; + PTW32_LIB ?= [ modules.peek user-config : PTW32_LIB ] ; + PTW32_INCLUDE ?= [ modules.peek site-config : PTW32_INCLUDE ] ; + PTW32_LIB ?= [ modules.peek site-config : PTW32_LIB ] ; + + if ! ( $(PTW32_INCLUDE) && $(PTW32_LIB) ) + { + if ! $(.notified) + { + echo "************************************************************" ; + echo "Trying to build Boost.Thread with pthread support." ; + echo "If you need pthread you should specify the paths." ; + echo "You can specify them in site-config.jam, user-config.jam" ; + echo "or in the environment." ; + echo "For example:" ; + echo "PTW32_INCLUDE=C:\\Program Files\\ptw32\\Pre-built2\\include" ; + echo "PTW32_LIB=C:\\Program Files\\ptw32\\Pre-built2\\lib" ; + echo "************************************************************" ; + .notified = true ; + } + } + else + { + local include_path = [ path.make $(PTW32_INCLUDE) ] ; + local lib_path = [ path.make $(PTW32_LIB) ] ; + local libname = pthread ; + if msvc in $(properties) + { + libname = $(libname)VC2.lib ; + } + if gcc in $(properties) + { + libname = lib$(libname)GC2.a ; + } + lib_path = [ path.glob $(lib_path) : $(libname) ] ; + if ! $(lib_path) + { + if ! $(.notified) + { + echo "************************************************************" ; + echo "Trying to build Boost.Thread with pthread support." ; + echo "But the library" $(libname) "could not be found in path" ; + echo $(PTW32_LIB) ; + echo "************************************************************" ; + .notified = true ; + } + } + else + { + result += $(include_path) ; + result += $(lib_path) ; + } + } + return $(result) ; +} + +rule usage-requirements ( properties * ) +{ + local result ; + if pthread in $(properties) + { + result += BOOST_THREAD_POSIX ; + if windows in $(properties) + { + result += [ win32_pthread_paths $(properties) ] ; + # TODO: What is for static linking? Is the also needed + # in that case? + } + } + if win32 in $(properties) + { + result += BOOST_THREAD_WIN32 ; + } + + #if ! vacpp in $(properties) || 11.1 in $(properties) || 12.1.0.1 in $(properties) || 12.1 in $(properties) + #{ + result += /boost/chrono//boost_chrono ; + #} + + return $(result) ; +} + +rule requirements ( properties * ) +{ + local result ; + + if pthread in $(properties) + { + result += BOOST_THREAD_POSIX ; + if windows in $(properties) + { + local paths = [ win32_pthread_paths $(properties) ] ; + if $(paths) + { + result += $(paths) ; + } + else + { + result = no ; + } + } + result += BOOST_THREAD_DONT_USE_CHRONO ; + if ! [ configure.builds has_atomic_flag_lockfree + : $(properties) : "lockfree boost::atomic_flag" ] { + result += /boost/atomic//boost_atomic ; + } + } else { + if win32 in $(properties) + { + result += BOOST_THREAD_WIN32 ; + } + result += BOOST_THREAD_USES_CHRONO ; + result += /boost/chrono//boost_chrono ; + } + + return $(result) ; +} + +alias thread_sources + : ## win32 sources ## + win32/thread.cpp + win32/tss_dll.cpp + win32/tss_pe.cpp + win32/thread_primitives.cpp + future.cpp + : ## requirements ## + win32 + ; + +alias thread_sources + : ## pthread sources ## + pthread/thread.cpp + pthread/once.cpp + future.cpp + : ## requirements ## + pthread + ; + +explicit thread_sources ; + +lib boost_thread + : thread_sources + : @requirements + : + : shared:BOOST_THREAD_USE_DLL=1 + static:BOOST_THREAD_USE_LIB=1 + @usage-requirements + ; + +boost-install boost_thread ; \ No newline at end of file diff --git a/libs/thread/build/has_atomic_flag_lockfree_test.cpp b/libs/thread/build/has_atomic_flag_lockfree_test.cpp new file mode 100644 index 0000000..ca69405 --- /dev/null +++ b/libs/thread/build/has_atomic_flag_lockfree_test.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2013, Petr Machata, Red Hat Inc. +// +// Use modification and distribution are subject to the boost Software +// License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). + +#include "../../../boost/atomic.hpp" +#include "../../../boost/static_assert.hpp" + +int main(int argc, char *argv[]) +{ + BOOST_STATIC_ASSERT(BOOST_ATOMIC_FLAG_LOCK_FREE); + return 0; +} diff --git a/libs/thread/src/future.cpp b/libs/thread/src/future.cpp new file mode 100644 index 0000000..a477e70 --- /dev/null +++ b/libs/thread/src/future.cpp @@ -0,0 +1,64 @@ +// (C) Copyright 2012 Vicente J. Botet Escriba +// Use, modification and distribution are subject to 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 + +#ifndef BOOST_NO_EXCEPTIONS + +#include +#include + +namespace boost +{ + + namespace thread_detail + { + + class future_error_category : + public boost::system::error_category + { + public: + virtual const char* name() const BOOST_NOEXCEPT; + virtual std::string message(int ev) const; + }; + + const char* + future_error_category::name() const BOOST_NOEXCEPT + { + return "future"; + } + + std::string + future_error_category::message(int ev) const + { + switch (BOOST_SCOPED_ENUM_NATIVE(future_errc)(ev)) + { + case future_errc::broken_promise: + return std::string("The associated promise has been destructed prior " + "to the associated state becoming ready."); + case future_errc::future_already_retrieved: + return std::string("The future has already been retrieved from " + "the promise or packaged_task."); + case future_errc::promise_already_satisfied: + return std::string("The state of the promise has already been set."); + case future_errc::no_state: + return std::string("Operation not permitted on an object without " + "an associated state."); + } + return std::string("unspecified future_errc value\n"); + } + future_error_category future_error_category_var; + } + + BOOST_THREAD_DECL + const system::error_category& + future_category() BOOST_NOEXCEPT + { + return thread_detail::future_error_category_var; + } + +} +#endif + diff --git a/libs/thread/src/pthread/once.cpp b/libs/thread/src/pthread/once.cpp new file mode 100644 index 0000000..3de1fe4 --- /dev/null +++ b/libs/thread/src/pthread/once.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2007 Anthony Williams +// +// 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 +#ifdef BOOST_THREAD_ONCE_ATOMIC +#include "./once_atomic.cpp" +#else +#define __STDC_CONSTANT_MACROS +#include +#include +#include +#include +#include +#include +#include // memcmp. +namespace boost +{ + namespace thread_detail + { + BOOST_THREAD_DECL uintmax_atomic_t once_global_epoch=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C; + BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER; + BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER; + + namespace + { + pthread_key_t epoch_tss_key; + pthread_once_t epoch_tss_key_flag=PTHREAD_ONCE_INIT; + + extern "C" + { + static void delete_epoch_tss_data(void* data) + { + free(data); + } + + static void create_epoch_tss_key() + { + BOOST_VERIFY(!pthread_key_create(&epoch_tss_key,delete_epoch_tss_data)); + } + } + +#if defined BOOST_THREAD_PATCH + const pthread_once_t pthread_once_init_value=PTHREAD_ONCE_INIT; + struct BOOST_THREAD_DECL delete_epoch_tss_key_on_dlclose_t + { + delete_epoch_tss_key_on_dlclose_t() + { + } + ~delete_epoch_tss_key_on_dlclose_t() + { + if(memcmp(&epoch_tss_key_flag, &pthread_once_init_value, sizeof(pthread_once_t))) + { + void* data = pthread_getspecific(epoch_tss_key); + if (data) + delete_epoch_tss_data(data); + pthread_key_delete(epoch_tss_key); + } + } + }; + delete_epoch_tss_key_on_dlclose_t delete_epoch_tss_key_on_dlclose; +#endif + } + + uintmax_atomic_t& get_once_per_thread_epoch() + { + BOOST_VERIFY(!pthread_once(&epoch_tss_key_flag,create_epoch_tss_key)); + void* data=pthread_getspecific(epoch_tss_key); + if(!data) + { + data=malloc(sizeof(thread_detail::uintmax_atomic_t)); + if(!data) BOOST_THROW_EXCEPTION(std::bad_alloc()); + BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data)); + *static_cast(data)=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C; + } + return *static_cast(data); + } + } + +} +#endif // diff --git a/libs/thread/src/pthread/once_atomic.cpp b/libs/thread/src/pthread/once_atomic.cpp new file mode 100644 index 0000000..69f5be8 --- /dev/null +++ b/libs/thread/src/pthread/once_atomic.cpp @@ -0,0 +1,91 @@ +// (C) Copyright 2013 Andrey Semashev +// (C) Copyright 2013 Vicente J. Botet Escriba +// +// 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) + +//#define __STDC_CONSTANT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + namespace thread_detail + { + + enum flag_states + { + uninitialized, in_progress, initialized + }; + + +#ifndef BOOST_THREAD_PROVIDES_ONCE_CXX11 + BOOST_STATIC_ASSERT_MSG(sizeof(atomic_int_type) == sizeof(atomic_type), "Boost.Thread: unsupported platform"); +#endif + + static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; + static pthread_cond_t once_cv = PTHREAD_COND_INITIALIZER; + + BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT + { + atomic_type& f = get_atomic_storage(flag); + if (f.load(memory_order_acquire) != initialized) + { + pthread::pthread_mutex_scoped_lock lk(&once_mutex); + if (f.load(memory_order_acquire) != initialized) + { + for (;;) + { + atomic_int_type expected = uninitialized; + if (f.compare_exchange_strong(expected, in_progress, memory_order_acq_rel, memory_order_acquire)) + { + // We have set the flag to in_progress + return true; + } + else if (expected == initialized) + { + // Another thread managed to complete the initialization + return false; + } + else + { + // Wait until the initialization is complete + //pthread::pthread_mutex_scoped_lock lk(&once_mutex); + BOOST_VERIFY(!posix::pthread_cond_wait(&once_cv, &once_mutex)); + } + } + } + } + return false; + } + + BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT + { + atomic_type& f = get_atomic_storage(flag); + { + pthread::pthread_mutex_scoped_lock lk(&once_mutex); + f.store(initialized, memory_order_release); + } + BOOST_VERIFY(!posix::pthread_cond_broadcast(&once_cv)); + } + + BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT + { + atomic_type& f = get_atomic_storage(flag); + { + pthread::pthread_mutex_scoped_lock lk(&once_mutex); + f.store(uninitialized, memory_order_release); + } + BOOST_VERIFY(!posix::pthread_cond_broadcast(&once_cv)); + } + + } // namespace thread_detail + +} // namespace boost diff --git a/libs/thread/src/pthread/thread.cpp b/libs/thread/src/pthread/thread.cpp new file mode 100644 index 0000000..c17aca2 --- /dev/null +++ b/libs/thread/src/pthread/thread.cpp @@ -0,0 +1,801 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007-8 Anthony Williams +// (C) Copyright 2011-2012 Vicente J. Botet Escriba +// +// 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 + +#include +#if defined BOOST_THREAD_USES_DATETIME +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GLIBC__ +#include +#elif defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +#elif defined BOOST_HAS_UNISTD_H +#include +#endif + +#if defined(__VXWORKS__) +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include // memcmp. + +namespace boost +{ + namespace detail + { + thread_data_base::~thread_data_base() + { + for (notify_list_t::iterator i = notify.begin(), e = notify.end(); + i != e; ++i) + { + i->second->unlock(); + i->first->notify_all(); + } +//#ifndef BOOST_NO_EXCEPTIONS + for (async_states_t::iterator i = async_states_.begin(), e = async_states_.end(); + i != e; ++i) + { + (*i)->notify_deferred(); + } +//#endif + } + + struct thread_exit_callback_node + { + boost::detail::thread_exit_function_base* func; + thread_exit_callback_node* next; + + thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, + thread_exit_callback_node* next_): + func(func_),next(next_) + {} + }; + + namespace + { +#ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 + boost::once_flag current_thread_tls_init_flag; +#else + boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; +#endif + pthread_key_t current_thread_tls_key; + + extern "C" + { + static void tls_destructor(void* data) + { + //boost::detail::thread_data_base* thread_info=static_cast(data); + boost::detail::thread_data_ptr thread_info = static_cast(data)->shared_from_this(); + + if(thread_info) + { + while(!thread_info->tss_data.empty() || thread_info->thread_exit_callbacks) + { + + while(thread_info->thread_exit_callbacks) + { + detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks; + thread_info->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + delete current_node->func; + } + delete current_node; + } + while (!thread_info->tss_data.empty()) + { + std::map::iterator current + = thread_info->tss_data.begin(); + if(current->second.func && (current->second.value!=0)) + { + (*current->second.caller)(current->second.func,current->second.value); + } + thread_info->tss_data.erase(current); + } + } + thread_info->self.reset(); + } + } + } + +#if defined BOOST_THREAD_PATCH + struct delete_current_thread_tls_key_on_dlclose_t + { + delete_current_thread_tls_key_on_dlclose_t() + { + } + ~delete_current_thread_tls_key_on_dlclose_t() + { + const boost::once_flag uninitialized = BOOST_ONCE_INIT; + if (memcmp(¤t_thread_tls_init_flag, &uninitialized, sizeof(boost::once_flag))) + { + void* data = pthread_getspecific(current_thread_tls_key); + if (data) + tls_destructor(data); + pthread_key_delete(current_thread_tls_key); + } + } + }; + delete_current_thread_tls_key_on_dlclose_t delete_current_thread_tls_key_on_dlclose; +#endif + void create_current_thread_tls_key() + { + BOOST_VERIFY(!pthread_key_create(¤t_thread_tls_key,&tls_destructor)); + } + } + + boost::detail::thread_data_base* get_current_thread_data() + { + boost::call_once(current_thread_tls_init_flag,&create_current_thread_tls_key); + return (boost::detail::thread_data_base*)pthread_getspecific(current_thread_tls_key); + } + + void set_current_thread_data(detail::thread_data_base* new_data) + { + boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); + BOOST_VERIFY(!pthread_setspecific(current_thread_tls_key,new_data)); + } + } + + namespace + { + extern "C" + { + static void* thread_proxy(void* param) + { + //boost::detail::thread_data_ptr thread_info = static_cast(param)->self; + boost::detail::thread_data_ptr thread_info = static_cast(param)->shared_from_this(); + thread_info->self.reset(); + detail::set_current_thread_data(thread_info.get()); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + BOOST_TRY + { +#endif + thread_info->run(); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + + } + BOOST_CATCH (thread_interrupted const&) + { + } +// Removed as it stops the debugger identifying the cause of the exception +// Unhandled exceptions still cause the application to terminate +// BOOST_CATCH(...) +// { +// throw; +// +// std::terminate(); +// } + BOOST_CATCH_END +#endif + detail::tls_destructor(thread_info.get()); + detail::set_current_thread_data(0); + boost::lock_guard lock(thread_info->data_mutex); + thread_info->done=true; + thread_info->done_condition.notify_all(); + + return 0; + } + } + } + namespace detail + { + struct externally_launched_thread: + detail::thread_data_base + { + externally_launched_thread() + { +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + interrupt_enabled=false; +#endif + } + ~externally_launched_thread() { + BOOST_ASSERT(notify.empty()); + notify.clear(); +//#ifndef BOOST_NO_EXCEPTIONS + BOOST_ASSERT(async_states_.empty()); + async_states_.clear(); +//#endif + } + void run() + {} + void notify_all_at_thread_exit(condition_variable*, mutex*) + {} + + private: + externally_launched_thread(externally_launched_thread&); + void operator=(externally_launched_thread&); + }; + + thread_data_base* make_external_thread_data() + { + thread_data_base* const me(detail::heap_new()); + me->self.reset(me); + set_current_thread_data(me); + return me; + } + + + thread_data_base* get_or_make_current_thread_data() + { + thread_data_base* current_thread_data(get_current_thread_data()); + if(!current_thread_data) + { + current_thread_data=make_external_thread_data(); + } + return current_thread_data; + } + + } + + + thread::thread() BOOST_NOEXCEPT + {} + + bool thread::start_thread_noexcept() + { + thread_info->self=thread_info; + int const res = pthread_create(&thread_info->thread_handle, 0, &thread_proxy, thread_info.get()); + if (res != 0) + { + thread_info->self.reset(); + return false; + } + return true; + } + + bool thread::start_thread_noexcept(const attributes& attr) + { + thread_info->self=thread_info; + const attributes::native_handle_type* h = attr.native_handle(); + int res = pthread_create(&thread_info->thread_handle, h, &thread_proxy, thread_info.get()); + if (res != 0) + { + thread_info->self.reset(); + return false; + } + int detached_state; + res = pthread_attr_getdetachstate(h, &detached_state); + if (res != 0) + { + thread_info->self.reset(); + return false; + } + if (PTHREAD_CREATE_DETACHED==detached_state) + { + detail::thread_data_ptr local_thread_info; + thread_info.swap(local_thread_info); + + if(local_thread_info) + { + //lock_guard lock(local_thread_info->data_mutex); + if(!local_thread_info->join_started) + { + //BOOST_VERIFY(!pthread_detach(local_thread_info->thread_handle)); + local_thread_info->join_started=true; + local_thread_info->joined=true; + } + } + } + return true; + } + + + + detail::thread_data_ptr thread::get_thread_info BOOST_PREVENT_MACRO_SUBSTITUTION () const + { + return thread_info; + } + + bool thread::join_noexcept() + { + detail::thread_data_ptr const local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + bool do_join=false; + + { + unique_lock lock(local_thread_info->data_mutex); + while(!local_thread_info->done) + { + local_thread_info->done_condition.wait(lock); + } + do_join=!local_thread_info->join_started; + + if(do_join) + { + local_thread_info->join_started=true; + } + else + { + while(!local_thread_info->joined) + { + local_thread_info->done_condition.wait(lock); + } + } + } + if(do_join) + { + void* result=0; + BOOST_VERIFY(!pthread_join(local_thread_info->thread_handle,&result)); + lock_guard lock(local_thread_info->data_mutex); + local_thread_info->joined=true; + local_thread_info->done_condition.notify_all(); + } + + if(thread_info==local_thread_info) + { + thread_info.reset(); + } + return true; + } + else + { + return false; + } + } + + bool thread::do_try_join_until_noexcept(detail::internal_platform_timepoint const &timeout, bool& res) + { + detail::thread_data_ptr const local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + bool do_join=false; + + { + unique_lock lock(local_thread_info->data_mutex); + while(!local_thread_info->done) + { + if(!local_thread_info->done_condition.do_wait_until(lock,timeout)) break; // timeout occurred + } + if(!local_thread_info->done) + { + res=false; + return true; + } + do_join=!local_thread_info->join_started; + + if(do_join) + { + local_thread_info->join_started=true; + } + else + { + while(!local_thread_info->joined) + { + local_thread_info->done_condition.wait(lock); + } + } + } + if(do_join) + { + void* result=0; + BOOST_VERIFY(!pthread_join(local_thread_info->thread_handle,&result)); + lock_guard lock(local_thread_info->data_mutex); + local_thread_info->joined=true; + local_thread_info->done_condition.notify_all(); + } + + if(thread_info==local_thread_info) + { + thread_info.reset(); + } + res=true; + return true; + } + else + { + return false; + } + } + + bool thread::joinable() const BOOST_NOEXCEPT + { + return (get_thread_info)()?true:false; + } + + + void thread::detach() + { + detail::thread_data_ptr local_thread_info; + thread_info.swap(local_thread_info); + + if(local_thread_info) + { + lock_guard lock(local_thread_info->data_mutex); + if(!local_thread_info->join_started) + { + BOOST_VERIFY(!pthread_detach(local_thread_info->thread_handle)); + local_thread_info->join_started=true; + local_thread_info->joined=true; + } + } + } + + namespace this_thread + { + namespace no_interruption_point + { + namespace hidden + { + void BOOST_THREAD_DECL sleep_for_internal(const detail::platform_duration& ts) + { + if (ts > detail::platform_duration::zero()) + { + // Use pthread_delay_np or nanosleep whenever possible here in the no_interruption_point + // namespace because they do not provide an interruption point. + # if defined(BOOST_HAS_PTHREAD_DELAY_NP) + # if defined(__IBMCPP__) || defined(_AIX) + BOOST_VERIFY(!pthread_delay_np(const_cast(&ts.getTs()))); + # else + BOOST_VERIFY(!pthread_delay_np(&ts.getTs())); + # endif + # elif defined(BOOST_HAS_NANOSLEEP) + nanosleep(&ts.getTs(), 0); + # else + // This should never be reached due to BOOST_THREAD_SLEEP_FOR_IS_STEADY + # endif + } + } + } + } + + void yield() BOOST_NOEXCEPT + { +# if defined(BOOST_HAS_SCHED_YIELD) + BOOST_VERIFY(!sched_yield()); +# elif defined(BOOST_HAS_PTHREAD_YIELD) + BOOST_VERIFY(!pthread_yield()); +//# elif defined BOOST_THREAD_USES_DATETIME +// ::boost::xtime xt; +// xtime_get(&xt, TIME_UTC_); +// sleep(xt); +// sleep_for(chrono::milliseconds(0)); +# else + mutex mx; + unique_lock lock(mx); + condition_variable cond; + cond.do_wait_until(lock, detail::internal_platform_clock::now()); +# endif + } + } + unsigned thread::hardware_concurrency() BOOST_NOEXCEPT + { +#if defined(PTW32_VERSION) || defined(__hpux) + return pthread_num_processors_np(); +#elif defined(__APPLE__) || defined(__FreeBSD__) + int count; + size_t size=sizeof(count); + return sysctlbyname("hw.ncpu",&count,&size,NULL,0)?0:count; +#elif defined(BOOST_HAS_UNISTD_H) && defined(_SC_NPROCESSORS_ONLN) + int const count=sysconf(_SC_NPROCESSORS_ONLN); + return (count>0)?count:0; +#elif defined(__VXWORKS__) + cpuset_t set = ::vxCpuEnabledGet(); + #ifdef __DCC__ + int i; + for( i = 0; set; ++i) + { + set &= set -1; + } + return(i); + #else + return (__builtin_popcount(set) ); + #endif +#elif defined(__GLIBC__) + return get_nprocs(); +#else + return 0; +#endif + } + + unsigned thread::physical_concurrency() BOOST_NOEXCEPT + { +#ifdef __linux__ + try { + using namespace std; + + ifstream proc_cpuinfo ("/proc/cpuinfo"); + + const string physical_id("physical id"), core_id("core id"); + + typedef std::pair core_entry; // [physical ID, core id] + + std::set cores; + + core_entry current_core_entry; + + string line; + while ( getline(proc_cpuinfo, line) ) { + if (line.empty()) + continue; + + vector key_val(2); + boost::split(key_val, line, boost::is_any_of(":")); + + if (key_val.size() != 2) + return hardware_concurrency(); + + string key = key_val[0]; + string value = key_val[1]; + boost::trim(key); + boost::trim(value); + + if (key == physical_id) { + current_core_entry.first = boost::lexical_cast(value); + continue; + } + + if (key == core_id) { + current_core_entry.second = boost::lexical_cast(value); + cores.insert(current_core_entry); + continue; + } + } + // Fall back to hardware_concurrency() in case + // /proc/cpuinfo is formatted differently than we expect. + return cores.size() != 0 ? cores.size() : hardware_concurrency(); + } catch(...) { + return hardware_concurrency(); + } +#elif defined(__APPLE__) + int count; + size_t size=sizeof(count); + return sysctlbyname("hw.physicalcpu",&count,&size,NULL,0)?0:count; +#else + return hardware_concurrency(); +#endif + } + +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + void thread::interrupt() + { + detail::thread_data_ptr const local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + local_thread_info->interrupt_requested=true; + if(local_thread_info->current_cond) + { + boost::pthread::pthread_mutex_scoped_lock internal_lock(local_thread_info->cond_mutex); + BOOST_VERIFY(!posix::pthread_cond_broadcast(local_thread_info->current_cond)); + } + } + } + + bool thread::interruption_requested() const BOOST_NOEXCEPT + { + detail::thread_data_ptr const local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + return local_thread_info->interrupt_requested; + } + else + { + return false; + } + } +#endif + + thread::native_handle_type thread::native_handle() + { + detail::thread_data_ptr const local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + lock_guard lk(local_thread_info->data_mutex); + return local_thread_info->thread_handle; + } + else + { + return pthread_t(); + } + } + + + +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + namespace this_thread + { + void interruption_point() + { +#ifndef BOOST_NO_EXCEPTIONS + boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); + if(thread_info && thread_info->interrupt_enabled) + { + lock_guard lg(thread_info->data_mutex); + if(thread_info->interrupt_requested) + { + thread_info->interrupt_requested=false; + throw thread_interrupted(); + } + } +#endif + } + + bool interruption_enabled() BOOST_NOEXCEPT + { + boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); + return thread_info && thread_info->interrupt_enabled; + } + + bool interruption_requested() BOOST_NOEXCEPT + { + boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); + if(!thread_info) + { + return false; + } + else + { + lock_guard lg(thread_info->data_mutex); + return thread_info->interrupt_requested; + } + } + + disable_interruption::disable_interruption() BOOST_NOEXCEPT: + interruption_was_enabled(interruption_enabled()) + { + if(interruption_was_enabled) + { + detail::get_current_thread_data()->interrupt_enabled=false; + } + } + + disable_interruption::~disable_interruption() BOOST_NOEXCEPT + { + if(detail::get_current_thread_data()) + { + detail::get_current_thread_data()->interrupt_enabled=interruption_was_enabled; + } + } + + restore_interruption::restore_interruption(disable_interruption& d) BOOST_NOEXCEPT + { + if(d.interruption_was_enabled) + { + detail::get_current_thread_data()->interrupt_enabled=true; + } + } + + restore_interruption::~restore_interruption() BOOST_NOEXCEPT + { + if(detail::get_current_thread_data()) + { + detail::get_current_thread_data()->interrupt_enabled=false; + } + } + } +#endif + + namespace detail + { + void add_thread_exit_function(thread_exit_function_base* func) + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + thread_exit_callback_node* const new_node= + heap_new(func,current_thread_data->thread_exit_callbacks); + current_thread_data->thread_exit_callbacks=new_node; + } + + tss_data_node* find_tss_data(void const* key) + { + detail::thread_data_base* const current_thread_data(get_current_thread_data()); + if(current_thread_data) + { + std::map::iterator current_node= + current_thread_data->tss_data.find(key); + if(current_node!=current_thread_data->tss_data.end()) + { + return ¤t_node->second; + } + } + return 0; + } + + void* get_tss_data(void const* key) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + return current_node->value; + } + return 0; + } + + void add_new_tss_node(void const* key, + detail::tss_data_node::cleanup_caller_t caller, + detail::tss_data_node::cleanup_func_t func, + void* tss_data) + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + current_thread_data->tss_data.insert(std::make_pair(key,tss_data_node(caller,func,tss_data))); + } + + void erase_tss_node(void const* key) + { + detail::thread_data_base* const current_thread_data(get_current_thread_data()); + if(current_thread_data) + { + current_thread_data->tss_data.erase(key); + } + } + + void set_tss_data(void const* key, + detail::tss_data_node::cleanup_caller_t caller, + detail::tss_data_node::cleanup_func_t func, + void* tss_data,bool cleanup_existing) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + if(cleanup_existing && current_node->func && (current_node->value!=0)) + { + (*current_node->caller)(current_node->func,current_node->value); + } + if(func || (tss_data!=0)) + { + current_node->caller=caller; + current_node->func=func; + current_node->value=tss_data; + } + else + { + erase_tss_node(key); + } + } + else if(func || (tss_data!=0)) + { + add_new_tss_node(key,caller,func,tss_data); + } + } + } + + BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock lk) + { + detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); + if(current_thread_data) + { + current_thread_data->notify_all_at_thread_exit(&cond, lk.release()); + } + } + +//#ifndef BOOST_NO_EXCEPTIONS +namespace detail { + + void BOOST_THREAD_DECL make_ready_at_thread_exit(shared_ptr as) + { + detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); + if(current_thread_data) + { + current_thread_data->make_ready_at_thread_exit(as); + } + } +} +//#endif + + +} diff --git a/libs/thread/src/tss_null.cpp b/libs/thread/src/tss_null.cpp new file mode 100644 index 0000000..e5e8151 --- /dev/null +++ b/libs/thread/src/tss_null.cpp @@ -0,0 +1,38 @@ +// (C) Copyright Michael Glassford 2004. +// (C) Copyright 2007 Anthony Williams +// Use, modification and distribution are subject to 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 + +#if defined(BOOST_THREAD_WIN32) && (defined(BOOST_THREAD_BUILD_LIB) || defined(BOOST_THREAD_TEST) || defined(UNDER_CE)) && (!defined(_MSC_VER) || defined(UNDER_CE)) + +namespace boost +{ + /* + This file is a "null" implementation of tss cleanup; it's + purpose is to to eliminate link errors in cases + where it is known that tss cleanup is not needed. + */ + + void tss_cleanup_implemented(void) + { + /* + This function's sole purpose is to cause a link error in cases where + automatic tss cleanup is not implemented by Boost.Threads as a + reminder that user code is responsible for calling the necessary + functions at the appropriate times (and for implementing an a + tss_cleanup_implemented() function to eliminate the linker's + missing symbol error). + + If Boost.Threads later implements automatic tss cleanup in cases + where it currently doesn't (which is the plan), the duplicate + symbol error will warn the user that their custom solution is no + longer needed and can be removed. + */ + } + +} + +#endif //defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_LIB) && !defined(_MSC_VER) diff --git a/libs/thread/src/win32/thread.cpp b/libs/thread/src/win32/thread.cpp new file mode 100644 index 0000000..000ab87 --- /dev/null +++ b/libs/thread/src/win32/thread.cpp @@ -0,0 +1,992 @@ +// 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) +// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007 David Deakins +// (C) Copyright 2011-2018 Vicente J. Botet Escriba + +//#define BOOST_THREAD_VERSION 3 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined BOOST_THREAD_USES_DATETIME +#include +#include +#endif +#include +#include +#include +#ifndef UNDER_CE +#include +#endif +#include +#include +#include + +#if BOOST_PLAT_WINDOWS_RUNTIME +#include +#include +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "runtimeobject.lib") +#endif + +namespace boost +{ + namespace detail + { + thread_data_base::~thread_data_base() + { + for (notify_list_t::iterator i = notify.begin(), e = notify.end(); + i != e; ++i) + { + i->second->unlock(); + i->first->notify_all(); + } +//#ifndef BOOST_NO_EXCEPTIONS + for (async_states_t::iterator i = async_states_.begin(), e = async_states_.end(); + i != e; ++i) + { + (*i)->notify_deferred(); + } +//#endif + } + } + + namespace + { +#ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11 + boost::once_flag current_thread_tls_init_flag; +#else + boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT; +#endif +#if defined(UNDER_CE) + // Windows CE does not define the TLS_OUT_OF_INDEXES constant. +#define TLS_OUT_OF_INDEXES 0xFFFFFFFF +#endif +#if !BOOST_PLAT_WINDOWS_RUNTIME + DWORD current_thread_tls_key=TLS_OUT_OF_INDEXES; +#else + __declspec(thread) boost::detail::thread_data_base* current_thread_data_base; +#endif + + void create_current_thread_tls_key() + { + tss_cleanup_implemented(); // if anyone uses TSS, we need the cleanup linked in +#if !BOOST_PLAT_WINDOWS_RUNTIME + current_thread_tls_key=TlsAlloc(); + BOOST_ASSERT(current_thread_tls_key!=TLS_OUT_OF_INDEXES); +#endif + } + + void cleanup_tls_key() + { +#if !BOOST_PLAT_WINDOWS_RUNTIME + if(current_thread_tls_key!=TLS_OUT_OF_INDEXES) + { + TlsFree(current_thread_tls_key); + current_thread_tls_key=TLS_OUT_OF_INDEXES; + } +#endif + } + + void set_current_thread_data(detail::thread_data_base* new_data) + { + boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); +#if BOOST_PLAT_WINDOWS_RUNTIME + current_thread_data_base = new_data; +#else + if (current_thread_tls_key != TLS_OUT_OF_INDEXES) + { + BOOST_VERIFY(TlsSetValue(current_thread_tls_key, new_data)); + } + else + { + BOOST_VERIFY(false); + //boost::throw_exception(thread_resource_error()); + } +#endif + } + } + + namespace detail + { + thread_data_base* get_current_thread_data() + { +#if BOOST_PLAT_WINDOWS_RUNTIME + return current_thread_data_base; +#else + if (current_thread_tls_key == TLS_OUT_OF_INDEXES) + { + return 0; + } + return (detail::thread_data_base*)TlsGetValue(current_thread_tls_key); +#endif + } + } + + namespace + { +#ifndef BOOST_HAS_THREADEX +// Windows CE doesn't define _beginthreadex + + struct ThreadProxyData + { + typedef unsigned (__stdcall* func)(void*); + func start_address_; + void* arglist_; + ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} + }; + + DWORD WINAPI ThreadProxy(LPVOID args) + { + boost::csbl::unique_ptr data(reinterpret_cast(args)); + DWORD ret=data->start_address_(data->arglist_); + return ret; + } + + inline uintptr_t _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), + void* arglist, unsigned initflag, unsigned* thrdaddr) + { + DWORD threadID; + ThreadProxyData* data = new ThreadProxyData(start_address,arglist); + HANDLE hthread=CreateThread(static_cast(security),stack_size,ThreadProxy, + data,initflag,&threadID); + if (hthread==0) { + delete data; + return 0; + } + *thrdaddr=threadID; + return reinterpret_cast(hthread); + } + +#endif + + } + + namespace detail + { + struct thread_exit_callback_node + { + boost::detail::thread_exit_function_base* func; + thread_exit_callback_node* next; + + thread_exit_callback_node(boost::detail::thread_exit_function_base* func_, + thread_exit_callback_node* next_): + func(func_),next(next_) + {} + }; + + } + +#if BOOST_PLAT_WINDOWS_RUNTIME + namespace detail + { + std::atomic_uint threadCount; + + bool win32::scoped_winrt_thread::start(thread_func address, void *parameter, unsigned int *thrdId) + { + Microsoft::WRL::ComPtr threadPoolFactory; + HRESULT hr = ::Windows::Foundation::GetActivationFactory( + Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), + &threadPoolFactory); + if (hr != S_OK) + { + return false; + } + + // Create event for tracking work item completion. + *thrdId = ++threadCount; + handle completionHandle = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS); + if (!completionHandle) + { + return false; + } + m_completionHandle = completionHandle; + + // Create new work item. + Microsoft::WRL::ComPtr workItem = + Microsoft::WRL::Callback, ABI::Windows::System::Threading::IWorkItemHandler, Microsoft::WRL::FtmBase>> + ([address, parameter, completionHandle](ABI::Windows::Foundation::IAsyncAction *) + { + // Add a reference since we need to access the completionHandle after the thread_start_function. + // This is to handle cases where detach() was called and run_thread_exit_callbacks() would end + // up closing the handle. + ::boost::detail::thread_data_base* const thread_info(reinterpret_cast<::boost::detail::thread_data_base*>(parameter)); + intrusive_ptr_add_ref(thread_info); + + __try + { + address(parameter); + } + __finally + { + SetEvent(completionHandle); + intrusive_ptr_release(thread_info); + } + return S_OK; + }); + + // Schedule work item on the threadpool. + Microsoft::WRL::ComPtr asyncAction; + hr = threadPoolFactory->RunWithPriorityAndOptionsAsync( + workItem.Get(), + ABI::Windows::System::Threading::WorkItemPriority_Normal, + ABI::Windows::System::Threading::WorkItemOptions_TimeSliced, + &asyncAction); + return hr == S_OK; + } + } +#endif + + namespace + { + void run_thread_exit_callbacks() + { + detail::thread_data_ptr current_thread_data(detail::get_current_thread_data(),false); + if(current_thread_data) + { + while(! current_thread_data->tss_data.empty() || current_thread_data->thread_exit_callbacks) + { + while(current_thread_data->thread_exit_callbacks) + { + detail::thread_exit_callback_node* const current_node=current_thread_data->thread_exit_callbacks; + current_thread_data->thread_exit_callbacks=current_node->next; + if(current_node->func) + { + (*current_node->func)(); + boost::detail::heap_delete(current_node->func); + } + boost::detail::heap_delete(current_node); + } + while (!current_thread_data->tss_data.empty()) + { + std::map::iterator current + = current_thread_data->tss_data.begin(); + if(current->second.func && (current->second.value!=0)) + { + (*current->second.caller)(current->second.func,current->second.value); + } + current_thread_data->tss_data.erase(current); + } + } + set_current_thread_data(0); + } + } + + unsigned __stdcall thread_start_function(void* param) + { + detail::thread_data_base* const thread_info(reinterpret_cast(param)); + set_current_thread_data(thread_info); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + BOOST_TRY + { +#endif + thread_info->run(); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + } + BOOST_CATCH(thread_interrupted const&) + { + } + // Unhandled exceptions still cause the application to terminate + BOOST_CATCH_END +#endif + run_thread_exit_callbacks(); + return 0; + } + } + + thread::thread() BOOST_NOEXCEPT + {} + + bool thread::start_thread_noexcept() + { +#if BOOST_PLAT_WINDOWS_RUNTIME + intrusive_ptr_add_ref(thread_info.get()); + if (!thread_info->thread_handle.start(&thread_start_function, thread_info.get(), &thread_info->id)) + { + intrusive_ptr_release(thread_info.get()); + return false; + } + return true; +#else + uintptr_t const new_thread=_beginthreadex(0,0,&thread_start_function,thread_info.get(),CREATE_SUSPENDED,&thread_info->id); + if(!new_thread) + { + return false; + } + intrusive_ptr_add_ref(thread_info.get()); + thread_info->thread_handle=(detail::win32::handle)(new_thread); + ResumeThread(thread_info->thread_handle); + return true; +#endif + } + + bool thread::start_thread_noexcept(const attributes& attr) + { +#if BOOST_PLAT_WINDOWS_RUNTIME + // Stack size isn't supported with Windows Runtime. + attr; + return start_thread_noexcept(); +#else + uintptr_t const new_thread=_beginthreadex(0,static_cast(attr.get_stack_size()),&thread_start_function,thread_info.get(), + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_info->id); + if(!new_thread) + { + return false; + } + intrusive_ptr_add_ref(thread_info.get()); + thread_info->thread_handle=(detail::win32::handle)(new_thread); + ResumeThread(thread_info->thread_handle); + return true; +#endif + } + + thread::thread(detail::thread_data_ptr data): + thread_info(data) + {} + + namespace + { + struct externally_launched_thread: + detail::thread_data_base + { + externally_launched_thread() + { + ++count; +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + interruption_enabled=false; +#endif + } + ~externally_launched_thread() { + BOOST_ASSERT(notify.empty()); + notify.clear(); +//#ifndef BOOST_NO_EXCEPTIONS + BOOST_ASSERT(async_states_.empty()); + async_states_.clear(); +//#endif + } + + void run() + {} + void notify_all_at_thread_exit(condition_variable*, mutex*) + {} + + private: + externally_launched_thread(externally_launched_thread&); + void operator=(externally_launched_thread&); + }; + + void make_external_thread_data() + { + externally_launched_thread* me=detail::heap_new(); + BOOST_TRY + { + set_current_thread_data(me); + } + BOOST_CATCH(...) + { + detail::heap_delete(me); + BOOST_RETHROW + } + BOOST_CATCH_END + } + + detail::thread_data_base* get_or_make_current_thread_data() + { + detail::thread_data_base* current_thread_data(detail::get_current_thread_data()); + if(!current_thread_data) + { + make_external_thread_data(); + current_thread_data=detail::get_current_thread_data(); + } + return current_thread_data; + } + } + + thread::id thread::get_id() const BOOST_NOEXCEPT + { +#if defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID + detail::thread_data_ptr local_thread_info=(get_thread_info)(); + if(!local_thread_info) + { + return 0; + } + return local_thread_info->id; +#else + return thread::id((get_thread_info)()); +#endif + } + + bool thread::joinable() const BOOST_NOEXCEPT + { + detail::thread_data_ptr local_thread_info = (get_thread_info)(); + if(!local_thread_info) + { + return false; + } + return true; + } + bool thread::join_noexcept() + { + detail::thread_data_ptr local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + this_thread::interruptible_wait(this->native_handle(), detail::internal_platform_timepoint::getMax()); + release_handle(); + return true; + } + else + { + return false; + } + } + + bool thread::do_try_join_until_noexcept(detail::internal_platform_timepoint const &timeout, bool& res) + { + detail::thread_data_ptr local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + if(!this_thread::interruptible_wait(this->native_handle(), timeout)) + { + res=false; + return true; + } + release_handle(); + res=true; + return true; + } + else + { + return false; + } + } + + void thread::detach() + { + release_handle(); + } + + void thread::release_handle() + { + thread_info=0; + } + +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + void thread::interrupt() + { + detail::thread_data_ptr local_thread_info=(get_thread_info)(); + if(local_thread_info) + { + local_thread_info->interrupt(); + } + } + + bool thread::interruption_requested() const BOOST_NOEXCEPT + { + detail::thread_data_ptr local_thread_info=(get_thread_info)(); + return local_thread_info.get() && (winapi::WaitForSingleObjectEx(local_thread_info->interruption_handle,0,0)==0); + } + +#endif + + unsigned thread::hardware_concurrency() BOOST_NOEXCEPT + { + detail::win32::system_info info; + detail::win32::get_system_info(&info); + return info.dwNumberOfProcessors; + } + + unsigned thread::physical_concurrency() BOOST_NOEXCEPT + { + // a bit too strict: Windows XP with SP3 would be sufficient +#if BOOST_PLAT_WINDOWS_RUNTIME \ + || ( BOOST_USE_WINAPI_VERSION <= BOOST_WINAPI_VERSION_WINXP ) \ + || ( ( defined(__MINGW32__) && !defined(__MINGW64__) ) && _WIN32_WINNT < 0x0600) + return 0; +#else + unsigned cores = 0; + DWORD size = 0; + + GetLogicalProcessorInformation(NULL, &size); + if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) + return 0; + const size_t Elements = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + + std::vector buffer(Elements); + if (GetLogicalProcessorInformation(&buffer.front(), &size) == FALSE) + return 0; + + + for (size_t i = 0; i < Elements; ++i) { + if (buffer[i].Relationship == RelationProcessorCore) + ++cores; + } + return cores; +#endif + } + + thread::native_handle_type thread::native_handle() + { + detail::thread_data_ptr local_thread_info=(get_thread_info)(); + if(!local_thread_info) + { + return detail::win32::invalid_handle_value; + } +#if BOOST_PLAT_WINDOWS_RUNTIME + // There is no 'real' Win32 handle so we return a handle that at least can be waited on. + return local_thread_info->thread_handle.waitable_handle(); +#else + return (detail::win32::handle)local_thread_info->thread_handle; +#endif + } + + detail::thread_data_ptr thread::get_thread_info BOOST_PREVENT_MACRO_SUBSTITUTION () const + { + return thread_info; + } + + namespace this_thread + { +#ifndef UNDER_CE +#if !BOOST_PLAT_WINDOWS_RUNTIME + namespace detail_ + { + typedef struct _REASON_CONTEXT { + ULONG Version; + DWORD Flags; + union { + LPWSTR SimpleReasonString; + struct { + HMODULE LocalizedReasonModule; + ULONG LocalizedReasonId; + ULONG ReasonStringCount; + LPWSTR *ReasonStrings; + } Detailed; + } Reason; + } REASON_CONTEXT, *PREASON_CONTEXT; + typedef BOOL (WINAPI *setwaitabletimerex_t)(HANDLE, const LARGE_INTEGER *, LONG, PTIMERAPCROUTINE, LPVOID, PREASON_CONTEXT, ULONG); + static inline BOOL WINAPI SetWaitableTimerEx_emulation(HANDLE hTimer, const LARGE_INTEGER *lpDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, PREASON_CONTEXT WakeContext, ULONG TolerableDelay) + { + return SetWaitableTimer(hTimer, lpDueTime, lPeriod, pfnCompletionRoutine, lpArgToCompletionRoutine, FALSE); + } +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail +#endif + static inline setwaitabletimerex_t SetWaitableTimerEx() + { + static setwaitabletimerex_t setwaitabletimerex_impl; + if(setwaitabletimerex_impl) + return setwaitabletimerex_impl; + void (*addr)()=(void (*)()) GetProcAddress( +#if !defined(BOOST_NO_ANSI_APIS) + GetModuleHandleA("KERNEL32.DLL"), +#else + GetModuleHandleW(L"KERNEL32.DLL"), +#endif + "SetWaitableTimerEx"); + if(addr) + setwaitabletimerex_impl=(setwaitabletimerex_t) addr; + else + setwaitabletimerex_impl=&SetWaitableTimerEx_emulation; + return setwaitabletimerex_impl; + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } +#endif +#endif + bool interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout) + { + detail::win32::handle handles[4]={0}; + unsigned handle_count=0; + unsigned wait_handle_index=~0U; +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + unsigned interruption_index=~0U; +#endif + unsigned timeout_index=~0U; + if(handle_to_wait_for!=detail::win32::invalid_handle_value) + { + wait_handle_index=handle_count; + handles[handle_count++]=handle_to_wait_for; + } +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + if(detail::get_current_thread_data() && detail::get_current_thread_data()->interruption_enabled) + { + interruption_index=handle_count; + handles[handle_count++]=detail::get_current_thread_data()->interruption_handle; + } +#endif + detail::win32::handle_manager timer_handle; + +#ifndef UNDER_CE +#if !BOOST_PLAT_WINDOWS_RUNTIME + // Preferentially use coalescing timers for better power consumption and timer accuracy + if(timeout != detail::internal_platform_timepoint::getMax()) + { + boost::intmax_t const time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); + timer_handle=CreateWaitableTimer(NULL,false,NULL); + if(timer_handle!=0) + { + ULONG const min_tolerable=32; // Empirical testing shows Windows ignores this when <= 26 + ULONG const max_tolerable=1000; + ULONG tolerable=min_tolerable; + if(time_left_msec/20>tolerable) // 5% + { + tolerable=static_cast(time_left_msec/20); + if(tolerable>max_tolerable) + tolerable=max_tolerable; + } + LARGE_INTEGER due_time={{0,0}}; + if(time_left_msec>0) + { + due_time.QuadPart=-(time_left_msec*10000); // negative indicates relative time + } + bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,NULL,tolerable)!=0; + if(set_time_succeeded) + { + timeout_index=handle_count; + handles[handle_count++]=timer_handle; + } + } + } +#endif +#endif + + bool const using_timer=timeout_index!=~0u; + boost::intmax_t time_left_msec(INFINITE); + if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) + { + time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); + if(time_left_msec < 0) + { + time_left_msec = 0; + } + } + + do + { + if(handle_count) + { + unsigned long const notified_index=winapi::WaitForMultipleObjectsEx(handle_count,handles,false,static_cast(time_left_msec), 0); + if(notified_indexinterruption_handle); + throw thread_interrupted(); + } +#endif + else if(notified_index==timeout_index) + { + return false; + } + } + } + else + { + detail::win32::sleep(static_cast(time_left_msec)); + } + + if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) + { + time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); + } + } + while(time_left_msec == INFINITE || time_left_msec > 0); + return false; + } + + namespace no_interruption_point + { + bool non_interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout) + { + detail::win32::handle handles[3]={0}; + unsigned handle_count=0; + unsigned wait_handle_index=~0U; + unsigned timeout_index=~0U; + if(handle_to_wait_for!=detail::win32::invalid_handle_value) + { + wait_handle_index=handle_count; + handles[handle_count++]=handle_to_wait_for; + } + detail::win32::handle_manager timer_handle; + +#ifndef UNDER_CE +#if !BOOST_PLAT_WINDOWS_RUNTIME + // Preferentially use coalescing timers for better power consumption and timer accuracy + if(timeout != detail::internal_platform_timepoint::getMax()) + { + boost::intmax_t const time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); + timer_handle=CreateWaitableTimer(NULL,false,NULL); + if(timer_handle!=0) + { + ULONG const min_tolerable=32; // Empirical testing shows Windows ignores this when <= 26 + ULONG const max_tolerable=1000; + ULONG tolerable=min_tolerable; + if(time_left_msec/20>tolerable) // 5% + { + tolerable=static_cast(time_left_msec/20); + if(tolerable>max_tolerable) + tolerable=max_tolerable; + } + LARGE_INTEGER due_time={{0,0}}; + if(time_left_msec>0) + { + due_time.QuadPart=-(time_left_msec*10000); // negative indicates relative time + } + bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,NULL,tolerable)!=0; + if(set_time_succeeded) + { + timeout_index=handle_count; + handles[handle_count++]=timer_handle; + } + } + } +#endif +#endif + + bool const using_timer=timeout_index!=~0u; + boost::intmax_t time_left_msec(INFINITE); + if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) + { + time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); + if(time_left_msec < 0) + { + time_left_msec = 0; + } + } + + do + { + if(handle_count) + { + unsigned long const notified_index=winapi::WaitForMultipleObjectsEx(handle_count,handles,false,static_cast(time_left_msec), 0); + if(notified_index(time_left_msec)); + } + + if(!using_timer && timeout != detail::internal_platform_timepoint::getMax()) + { + time_left_msec = (timeout - detail::internal_platform_clock::now()).getMs(); + } + } + while(time_left_msec == INFINITE || time_left_msec > 0); + return false; + } + } + + thread::id get_id() BOOST_NOEXCEPT + { +#if defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID +#if BOOST_PLAT_WINDOWS_RUNTIME + detail::thread_data_base* current_thread_data(detail::get_current_thread_data()); + if (current_thread_data) + { + return current_thread_data->id; + } +#endif + return winapi::GetCurrentThreadId(); +#else + return thread::id(get_or_make_current_thread_data()); +#endif + } + +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + void interruption_point() + { + if(interruption_enabled() && interruption_requested()) + { + winapi::ResetEvent(detail::get_current_thread_data()->interruption_handle); + throw thread_interrupted(); + } + } + + bool interruption_enabled() BOOST_NOEXCEPT + { + return detail::get_current_thread_data() && detail::get_current_thread_data()->interruption_enabled; + } + + bool interruption_requested() BOOST_NOEXCEPT + { + return detail::get_current_thread_data() && (winapi::WaitForSingleObjectEx(detail::get_current_thread_data()->interruption_handle,0,0)==0); + } +#endif + + void yield() BOOST_NOEXCEPT + { + detail::win32::sleep(0); + } + +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + disable_interruption::disable_interruption() BOOST_NOEXCEPT: + interruption_was_enabled(interruption_enabled()) + { + if(interruption_was_enabled) + { + detail::get_current_thread_data()->interruption_enabled=false; + } + } + + disable_interruption::~disable_interruption() BOOST_NOEXCEPT + { + if(detail::get_current_thread_data()) + { + detail::get_current_thread_data()->interruption_enabled=interruption_was_enabled; + } + } + + restore_interruption::restore_interruption(disable_interruption& d) BOOST_NOEXCEPT + { + if(d.interruption_was_enabled) + { + detail::get_current_thread_data()->interruption_enabled=true; + } + } + + restore_interruption::~restore_interruption() BOOST_NOEXCEPT + { + if(detail::get_current_thread_data()) + { + detail::get_current_thread_data()->interruption_enabled=false; + } + } +#endif + } + + namespace detail + { + void add_thread_exit_function(thread_exit_function_base* func) + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + thread_exit_callback_node* const new_node= + heap_new( + func,current_thread_data->thread_exit_callbacks); + current_thread_data->thread_exit_callbacks=new_node; + } + + tss_data_node* find_tss_data(void const* key) + { + detail::thread_data_base* const current_thread_data(get_current_thread_data()); + if(current_thread_data) + { + std::map::iterator current_node= + current_thread_data->tss_data.find(key); + if(current_node!=current_thread_data->tss_data.end()) + { + return ¤t_node->second; + } + } + return NULL; + } + + void* get_tss_data(void const* key) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + return current_node->value; + } + return NULL; + } + + void add_new_tss_node(void const* key, + detail::tss_data_node::cleanup_caller_t caller, + detail::tss_data_node::cleanup_func_t func, + void* tss_data) + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + current_thread_data->tss_data.insert(std::make_pair(key,tss_data_node(caller,func,tss_data))); + } + + void erase_tss_node(void const* key) + { + detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data()); + current_thread_data->tss_data.erase(key); + } + + void set_tss_data(void const* key, + detail::tss_data_node::cleanup_caller_t caller, + detail::tss_data_node::cleanup_func_t func, + void* tss_data,bool cleanup_existing) + { + if(tss_data_node* const current_node=find_tss_data(key)) + { + if(cleanup_existing && current_node->func && (current_node->value!=0)) + { + (*current_node->caller)(current_node->func,current_node->value); + } + if(func || (tss_data!=0)) + { + current_node->caller=caller; + current_node->func=func; + current_node->value=tss_data; + } + else + { + erase_tss_node(key); + } + } + else if(func || (tss_data!=0)) + { + add_new_tss_node(key,caller,func,tss_data); + } + } + } + + BOOST_THREAD_DECL void __cdecl on_process_enter() + {} + + BOOST_THREAD_DECL void __cdecl on_thread_enter() + {} + + BOOST_THREAD_DECL void __cdecl on_process_exit() + { + boost::cleanup_tls_key(); + } + + BOOST_THREAD_DECL void __cdecl on_thread_exit() + { + boost::run_thread_exit_callbacks(); + } + + BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock lk) + { + detail::thread_data_base* const current_thread_data(detail::get_current_thread_data()); + if(current_thread_data) + { + current_thread_data->notify_all_at_thread_exit(&cond, lk.release()); + } + } +} + diff --git a/libs/thread/src/win32/thread_primitives.cpp b/libs/thread/src/win32/thread_primitives.cpp new file mode 100644 index 0000000..ea8ff36 --- /dev/null +++ b/libs/thread/src/win32/thread_primitives.cpp @@ -0,0 +1,156 @@ +// thread_primitives.cpp +// +// (C) Copyright 2018 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) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace detail { +namespace win32 { + +#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +// Directly use API from Vista and later +BOOST_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64 = &::boost::winapi::GetTickCount64; + +#else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +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(64) static get_tick_count64_state g_state; + +//! Artifical implementation of GetTickCount64 +ticks_type BOOST_WINAPI_WINAPI_CC get_tick_count64() +{ + 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; + } +} + +ticks_type BOOST_WINAPI_WINAPI_CC get_tick_count_init() +{ + boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); + if (hKernel32) + { + // GetProcAddress returns a pointer to some function. It can return + // pointers to different functions, so it has to return something that is + // suitable to store any pointer to function. Microsoft chose FARPROC, + // which is int (WINAPI *)() on 32-bit Windows. The user is supposed to + // know the signature of the function he requests and perform a cast + // (which is a nop on this platform). The result is a pointer to function + // with the required signature, which is bitwise equal to what + // GetProcAddress returned. + // However, gcc >= 8 warns about that. +#if defined(BOOST_GCC) && BOOST_GCC >= 80000 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + boost::detail::win32::detail::gettickcount64_t p = + (boost::detail::win32::detail::gettickcount64_t)boost::winapi::get_proc_address(hKernel32, "GetTickCount64"); +#if defined(BOOST_GCC) && BOOST_GCC >= 80000 +#pragma GCC diagnostic pop +#endif + if (p) + { + // Use native API + boost::detail::interlocked_write_release((void**)&gettickcount64, (void*)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); + + boost::detail::interlocked_write_release((void**)&gettickcount64, (void*)&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_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64 = &get_tick_count_init; + +#endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 + +} // namespace win32 +} // namespace detail +} // namespace boost diff --git a/libs/thread/src/win32/tss_dll.cpp b/libs/thread/src/win32/tss_dll.cpp new file mode 100644 index 0000000..6a85499 --- /dev/null +++ b/libs/thread/src/win32/tss_dll.cpp @@ -0,0 +1,87 @@ +// (C) Copyright Michael Glassford 2004. +// Use, modification and distribution are subject to 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 +#include + + +#if defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_DLL) + + #include + + #include + + #if defined(BOOST_BORLANDC) + extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE /*hInstance*/, DWORD dwReason, LPVOID /*lpReserved*/) + #elif defined(BOOST_EMBTC) + extern "C" int _libmain(DWORD dwReason) + #elif defined(_WIN32_WCE) + extern "C" BOOL WINAPI DllMain(HANDLE /*hInstance*/, DWORD dwReason, LPVOID /*lpReserved*/) + #else + extern "C" BOOL WINAPI DllMain(HINSTANCE /*hInstance*/, DWORD dwReason, LPVOID /*lpReserved*/) + #endif + { + switch(dwReason) + { + case DLL_PROCESS_ATTACH: + { + boost::on_process_enter(); + boost::on_thread_enter(); + break; + } + + case DLL_THREAD_ATTACH: + { + boost::on_thread_enter(); + break; + } + + case DLL_THREAD_DETACH: + { + boost::on_thread_exit(); + break; + } + + case DLL_PROCESS_DETACH: + { + boost::on_thread_exit(); + boost::on_process_exit(); + break; + } + } + + return TRUE; + } + +namespace boost +{ + void tss_cleanup_implemented() + { + /* + This function's sole purpose is to cause a link error in cases where + automatic tss cleanup is not implemented by Boost.Threads as a + reminder that user code is responsible for calling the necessary + functions at the appropriate times (and for implementing an a + tss_cleanup_implemented() function to eliminate the linker's + missing symbol error). + + If Boost.Threads later implements automatic tss cleanup in cases + where it currently doesn't (which is the plan), the duplicate + symbol error will warn the user that their custom solution is no + longer needed and can be removed. + */ + } +} + +#else //defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_DLL) + +#ifdef _MSC_VER +// Prevent LNK4221 warning with link=static +namespace boost { namespace link_static_warning_inhibit { + extern __declspec(dllexport) void foo() { } +} } +#endif + +#endif //defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_DLL) diff --git a/libs/thread/src/win32/tss_pe.cpp b/libs/thread/src/win32/tss_pe.cpp new file mode 100644 index 0000000..5a4c764 --- /dev/null +++ b/libs/thread/src/win32/tss_pe.cpp @@ -0,0 +1,346 @@ +// $Id$ +// (C) Copyright Aaron W. LaFramboise, Roland Schwarz, Michael Glassford 2004. +// (C) Copyright 2007 Roland Schwarz +// (C) Copyright 2007 Anthony Williams +// (C) Copyright 2007 David Deakins +// Use, modification and distribution are subject to 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 +#include + +#if defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_LIB) + +#if (defined(__MINGW32__) && !defined(_WIN64)) || defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) + +#include + +#include + +#include + +namespace boost +{ + void tss_cleanup_implemented() {} +} + +namespace { + void NTAPI on_tls_callback(void* , DWORD dwReason, PVOID ) + { + switch (dwReason) + { + case DLL_THREAD_DETACH: + { + boost::on_thread_exit(); + break; + } + } + } +} + +#if defined(__MINGW64__) || (__MINGW64_VERSION_MAJOR) || (__MINGW32__) || (__MINGW32_MAJOR_VERSION >3) || \ + ((__MINGW32_MAJOR_VERSION==3) && (__MINGW32_MINOR_VERSION>=18)) +extern "C" +{ + PIMAGE_TLS_CALLBACK __crt_xl_tls_callback__ __attribute__ ((section(".CRT$XLB"))) = on_tls_callback; +} +#else +extern "C" { + + void (* after_ctors )() __attribute__((section(".ctors"))) = boost::on_process_enter; + void (* before_dtors)() __attribute__((section(".dtors"))) = boost::on_thread_exit; + void (* after_dtors )() __attribute__((section(".dtors.zzz"))) = boost::on_process_exit; + + ULONG __tls_index__ = 0; + char __tls_end__ __attribute__((section(".tls$zzz"))) = 0; + char __tls_start__ __attribute__((section(".tls"))) = 0; + + + PIMAGE_TLS_CALLBACK __crt_xl_start__ __attribute__ ((section(".CRT$XLA"))) = 0; + PIMAGE_TLS_CALLBACK __crt_xl_end__ __attribute__ ((section(".CRT$XLZ"))) = 0; +} +extern "C" const IMAGE_TLS_DIRECTORY32 _tls_used __attribute__ ((section(".rdata$T"))) = +{ + (DWORD) &__tls_start__, + (DWORD) &__tls_end__, + (DWORD) &__tls_index__, + (DWORD) (&__crt_xl_start__+1), + (DWORD) 0, + (DWORD) 0 +}; +#endif + + +#elif defined(_MSC_VER) && !defined(UNDER_CE) + + #include + + #include + + #include + + +// _pRawDllMainOrig can be defined by including boost/thread/win32/mfc_thread_init.hpp +// into your dll; it ensures that MFC-Dll-initialization will be done properly +// The following code is adapted from the MFC-Dll-init code +/* + * _pRawDllMainOrig MUST be an extern const variable, which will be aliased to + * _pDefaultRawDllMainOrig if no real user definition is present, thanks to the + * alternatename directive. + */ + +// work at least with _MSC_VER 1500 (MSVC++ 9.0, VS 2008) +#if (_MSC_VER >= 1500) + +extern "C" { +extern BOOL (WINAPI * const _pRawDllMainOrig)(HINSTANCE, DWORD, LPVOID); +extern BOOL (WINAPI * const _pDefaultRawDllMainOrig)(HINSTANCE, DWORD, LPVOID) = NULL; +#if defined (_M_IX86) +#pragma comment(linker, "/alternatename:__pRawDllMainOrig=__pDefaultRawDllMainOrig") +#elif defined (_M_X64) || defined (_M_ARM) || defined (_M_ARM64) +#pragma comment(linker, "/alternatename:_pRawDllMainOrig=_pDefaultRawDllMainOrig") +#else /* unknown Windows target (not x86, x64, ARM, ARM64) */ +#error Unsupported platform +#endif /* defined (_M_X64) || defined (_M_ARM) || defined (_M_ARM64) */ +} + +#endif + + + + + //Definitions required by implementation + #if (_MSC_VER < 1300) || ((_MSC_VER > 1900) && (_MSC_VER < 1910)) // 1300 == VC++ 7.0, 1900 == VC++ 14.0, 1910 == VC++ 2017 + typedef void ( __cdecl *_PVFV_ )(); + typedef void ( __cdecl *_PIFV_ )(); + #define INIRETSUCCESS_V + #define INIRETSUCCESS_I + #define PVAPI_V void __cdecl + #define PVAPI_I void __cdecl + #elif (_MSC_VER >= 1910) + typedef void ( __cdecl *_PVFV_ )(); + typedef int ( __cdecl *_PIFV_ )(); + #define INIRETSUCCESS_V + #define INIRETSUCCESS_I 0 + #define PVAPI_V void __cdecl + #define PVAPI_I int __cdecl + #else + typedef int ( __cdecl *_PVFV_ )(); + typedef int ( __cdecl *_PIFV_ )(); + #define INIRETSUCCESS_V 0 + #define INIRETSUCCESS_I 0 + #define PVAPI_V int __cdecl + #define PVAPI_I int __cdecl + #endif + + typedef void (NTAPI* _TLSCB)(HINSTANCE, DWORD, PVOID); + + //Symbols for connection to the runtime environment + + extern "C" + { + extern DWORD _tls_used; //the tls directory (located in .rdata segment) + extern _TLSCB __xl_a[], __xl_z[]; //tls initializers */ + } + + namespace + { + //Forward declarations + + static PVAPI_I on_tls_prepare(); + static PVAPI_V on_process_init(); + static PVAPI_V on_process_term(); + static void NTAPI on_tls_callback(HINSTANCE, DWORD, PVOID); + } + + namespace boost + { + //The .CRT$Xxx information is taken from Codeguru: + //http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c6945__2/ + + // Variables below are not referenced anywhere and + // to not be optimized away has to have external linkage + +#if (_MSC_VER >= 1400) +#pragma section(".CRT$XIU",long,read) +#pragma section(".CRT$XCU",long,read) +#pragma section(".CRT$XTU",long,read) +#pragma section(".CRT$XLC",long,read) + extern const __declspec(allocate(".CRT$XLC")) _TLSCB p_tls_callback = on_tls_callback; + extern const __declspec(allocate(".CRT$XIU")) _PIFV_ p_tls_prepare = on_tls_prepare; + extern const __declspec(allocate(".CRT$XCU")) _PVFV_ p_process_init = on_process_init; + extern const __declspec(allocate(".CRT$XTU")) _PVFV_ p_process_term = on_process_term; +#else + #if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 + # pragma data_seg(push, old_seg) + #endif + //Callback to run tls glue code first. + //I don't think it is necessary to run it + //at .CRT$XIB level, since we are only + //interested in thread detachement. But + //this could be changed easily if required. + + #pragma data_seg(".CRT$XIU") + extern const _PIFV_ p_tls_prepare = on_tls_prepare; + #pragma data_seg() + + //Callback after all global ctors. + + #pragma data_seg(".CRT$XCU") + extern const _PVFV_ p_process_init = on_process_init; + #pragma data_seg() + + //Callback for tls notifications. + + #pragma data_seg(".CRT$XLB") + extern const _TLSCB p_thread_callback = on_tls_callback; + #pragma data_seg() + //Callback for termination. + + #pragma data_seg(".CRT$XTU") + extern const _PVFV_ p_process_term = on_process_term; + #pragma data_seg() + #if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 + # pragma data_seg(pop, old_seg) + #endif +#endif + } // namespace boost + + namespace + { +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable:4189) +#endif + + PVAPI_I on_tls_prepare() + { + //The following line has an important side effect: + //if the TLS directory is not already there, it will + //be created by the linker. In other words, it forces a tls + //directory to be generated by the linker even when static tls + //(i.e. __declspec(thread)) is not used. + //The volatile should prevent the optimizer + //from removing the reference. + + DWORD volatile dw = _tls_used; + + #if (_MSC_VER < 1300) // 1300 == VC++ 7.0 + _TLSCB* pfbegin = __xl_a; + _TLSCB* pfend = __xl_z; + _TLSCB* pfdst = pfbegin; + //pfdst = (_TLSCB*)_tls_used.AddressOfCallBacks; + + //The following loop will merge the address pointers + //into a contiguous area, since the tlssup code seems + //to require this (at least on MSVC 6) + + while (pfbegin < pfend) + { + if (*pfbegin != 0) + { + *pfdst = *pfbegin; + ++pfdst; + } + ++pfbegin; + } + + *pfdst = 0; + #endif + + return INIRETSUCCESS_I; + } +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + + PVAPI_V on_process_init() + { + //Schedule on_thread_exit() to be called for the main + //thread before destructors of global objects have been + //called. + + //It will not be run when 'quick' exiting the + //library; however, this is the standard behaviour + //for destructors of global objects, so that + //shouldn't be a problem. + + atexit(boost::on_thread_exit); + + //Call Boost process entry callback here + + boost::on_process_enter(); + + return INIRETSUCCESS_V; + } + + PVAPI_V on_process_term() + { + boost::on_process_exit(); + return INIRETSUCCESS_V; + } + + void NTAPI on_tls_callback(HINSTANCE /*h*/, DWORD dwReason, PVOID /*pv*/) + { + switch (dwReason) + { + case DLL_THREAD_DETACH: + boost::on_thread_exit(); + break; + } + } + +#if (_MSC_VER >= 1500) + BOOL WINAPI dll_callback(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +#else + BOOL WINAPI dll_callback(HINSTANCE, DWORD dwReason, LPVOID) +#endif + { + switch (dwReason) + { + case DLL_THREAD_DETACH: + boost::on_thread_exit(); + break; + case DLL_PROCESS_DETACH: + boost::on_process_exit(); + break; + } + +#if (_MSC_VER >= 1500) + if( _pRawDllMainOrig ) + { + return _pRawDllMainOrig(hInstance, dwReason, lpReserved); + } +#endif + return true; + } + } //namespace + +extern "C" +{ + extern BOOL (WINAPI * const _pRawDllMain)(HINSTANCE, DWORD, LPVOID)=&dll_callback; +} +namespace boost +{ + void tss_cleanup_implemented() + { + /* + This function's sole purpose is to cause a link error in cases where + automatic tss cleanup is not implemented by Boost.Threads as a + reminder that user code is responsible for calling the necessary + functions at the appropriate times (and for implementing an a + tss_cleanup_implemented() function to eliminate the linker's + missing symbol error). + + If Boost.Threads later implements automatic tss cleanup in cases + where it currently doesn't (which is the plan), the duplicate + symbol error will warn the user that their custom solution is no + longer needed and can be removed. + */ + } +} + +#endif //defined(_MSC_VER) && !defined(UNDER_CE) + +#endif //defined(BOOST_THREAD_WIN32) && defined(BOOST_THREAD_BUILD_LIB)