This commit is contained in:
2026-03-23 20:54:41 +08:00
commit e13b3650e9
4596 changed files with 1015768 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_ARGS_HPP_INCLUDED
#define BOOST_NOWIDE_ARGS_HPP_INCLUDED
#include <boost/config.hpp>
#ifdef BOOST_WINDOWS
#include <boost/nowide/stackstring.hpp>
#include <boost/nowide/windows.hpp>
#include <stdexcept>
#include <vector>
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
class args
{
public:
args(int&, char**&)
{}
args(int&, char**&, char**&)
{}
};
#else
///
/// \brief \c args is a class that temporarily replaces standard main() function arguments with their
/// equal, but UTF-8 encoded values under Microsoft Windows for the lifetime of the instance.
///
/// The class uses \c GetCommandLineW(), \c CommandLineToArgvW() and \c GetEnvironmentStringsW()
/// in order to obtain Unicode-encoded values.
/// It does not relate to actual values of argc, argv and env under Windows.
///
/// It restores the original values in its destructor (usually at the end of the \c main function).
///
/// If any of the system calls fails, an exception of type std::runtime_error will be thrown
/// and argc, argv, env remain unchanged.
///
/// \note The class owns the memory of the newly allocated strings.
/// So you need to keep it alive as long as you use the values.
///
/// Usage:
/// \code
/// int main(int argc, char** argv, char** env) {
/// boost::nowide::args _(argc, argv, env); // Note the _ as a "don't care" name for the instance
/// // Use argv and env as usual, they are now UTF-8 encoded on Windows
/// return 0; // Memory held by args is released
/// }
/// \endcode
class args
{
public:
///
/// Fix command line arguments
///
args(int& argc, char**& argv) :
old_argc_(argc), old_argv_(argv), old_env_(0), old_argc_ptr_(&argc), old_argv_ptr_(&argv), old_env_ptr_(0)
{
fix_args(argc, argv);
}
///
/// Fix command line arguments and environment
///
args(int& argc, char**& argv, char**& env) :
old_argc_(argc), old_argv_(argv), old_env_(env), old_argc_ptr_(&argc), old_argv_ptr_(&argv),
old_env_ptr_(&env)
{
fix_args(argc, argv);
fix_env(env);
}
///
/// Restore original argc, argv, env values, if changed
///
~args()
{
if(old_argc_ptr_)
*old_argc_ptr_ = old_argc_;
if(old_argv_ptr_)
*old_argv_ptr_ = old_argv_;
if(old_env_ptr_)
*old_env_ptr_ = old_env_;
}
private:
class wargv_ptr
{
wchar_t** p;
int argc;
public:
wargv_ptr()
{
p = CommandLineToArgvW(GetCommandLineW(), &argc);
}
~wargv_ptr()
{
if(p)
LocalFree(p);
}
wargv_ptr(const wargv_ptr&) = delete;
wargv_ptr& operator=(const wargv_ptr&) = delete;
int size() const
{
return argc;
}
operator bool() const
{
return p != nullptr;
}
const wchar_t* operator[](size_t i) const
{
return p[i];
}
};
class wenv_ptr
{
wchar_t* p;
public:
wenv_ptr() : p(GetEnvironmentStringsW())
{}
~wenv_ptr()
{
if(p)
FreeEnvironmentStringsW(p);
}
wenv_ptr(const wenv_ptr&) = delete;
wenv_ptr& operator=(const wenv_ptr&) = delete;
operator const wchar_t*() const
{
return p;
}
};
void fix_args(int& argc, char**& argv)
{
const wargv_ptr wargv;
if(!wargv)
throw std::runtime_error("Could not get command line!");
args_.resize(wargv.size() + 1, 0);
arg_values_.resize(wargv.size());
for(int i = 0; i < wargv.size(); i++)
args_[i] = arg_values_[i].convert(wargv[i]);
argc = wargv.size();
argv = &args_[0];
}
void fix_env(char**& env)
{
const wenv_ptr wstrings;
if(!wstrings)
throw std::runtime_error("Could not get environment strings!");
const wchar_t* wstrings_end = 0;
int count = 0;
for(wstrings_end = wstrings; *wstrings_end; wstrings_end += wcslen(wstrings_end) + 1)
count++;
env_.convert(wstrings, wstrings_end);
envp_.resize(count + 1, 0);
char* p = env_.get();
int pos = 0;
for(int i = 0; i < count; i++)
{
if(*p != '=')
envp_[pos++] = p;
p += strlen(p) + 1;
}
env = &envp_[0];
}
std::vector<char*> args_;
std::vector<short_stackstring> arg_values_;
stackstring env_;
std::vector<char*> envp_;
int old_argc_;
char** old_argv_;
char** old_env_;
int* old_argc_ptr_;
char*** old_argv_ptr_;
char*** old_env_ptr_;
};
#endif
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,119 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2019 - 2022 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_CONFIG_HPP_INCLUDED
#define BOOST_NOWIDE_CONFIG_HPP_INCLUDED
/// @file
#include <boost/config.hpp>
#include <boost/nowide/replacement.hpp>
#include <boost/version.hpp>
//! @cond Doxygen_Suppress
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK)
#ifdef BOOST_NOWIDE_SOURCE
#define BOOST_NOWIDE_DECL BOOST_SYMBOL_EXPORT
#else
#define BOOST_NOWIDE_DECL BOOST_SYMBOL_IMPORT
#endif // BOOST_NOWIDE_SOURCE
#else
#define BOOST_NOWIDE_DECL
#endif // BOOST_NOWIDE_DYN_LINK
// Automatically link to the correct build variant where possible.
#if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_NOWIDE_NO_LIB) && !defined(BOOST_NOWIDE_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_nowide
//
// If we're importing code from a dll, then tell auto_link.hpp about it:
//
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK)
#define BOOST_DYN_LINK
#endif
//
// And include the header that does the work:
//
#include <boost/config/auto_link.hpp>
#endif // auto-linking disabled
//! @endcond
/// @def BOOST_NOWIDE_USE_WCHAR_OVERLOADS
/// @brief Whether to use the wchar_t* overloads in fstream-classes.
///
/// Enabled by default on Windows and Cygwin as the latter may use wchar_t in filesystem::path.
#ifndef BOOST_NOWIDE_USE_WCHAR_OVERLOADS
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(BOOST_NOWIDE_DOXYGEN)
#define BOOST_NOWIDE_USE_WCHAR_OVERLOADS 1
#else
#define BOOST_NOWIDE_USE_WCHAR_OVERLOADS 0
#endif
#endif
/// @def BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
/// @brief Define to 1 to use the class from <filebuf.hpp> that is used on Windows.
///
/// - On Windows: No effect, always overwritten to 1
/// - Others (including Cygwin): Defaults to the value of #BOOST_NOWIDE_USE_WCHAR_OVERLOADS if not set.
///
/// When set to 0 boost::nowide::basic_filebuf will be an alias for std::basic_filebuf.
///
/// Affects boost::nowide::basic_filebuf,
/// boost::nowide::basic_ofstream, boost::nowide::basic_ifstream, boost::nowide::basic_fstream
#if defined(BOOST_WINDOWS) || defined(BOOST_NOWIDE_DOXYGEN)
#ifdef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
#undef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
#endif
#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 1
#elif !defined(BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT)
#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT BOOST_NOWIDE_USE_WCHAR_OVERLOADS
#endif
//! @cond Doxygen_Suppress
#if BOOST_VERSION < 106500 && defined(__GNUC__) && __GNUC__ >= 7
#define BOOST_NOWIDE_FALLTHROUGH __attribute__((fallthrough))
#else
#define BOOST_NOWIDE_FALLTHROUGH BOOST_FALLTHROUGH
#endif
// The std::codecvt<char16/32_t, char, std::mbstate_t> are deprecated in C++20
// These macros can suppress this warning
#if defined(_MSC_VER)
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN __pragma(warning(push)) __pragma(warning(disable : 4996))
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END __pragma(warning(pop))
#elif(__cplusplus >= 202002L) && defined(__clang__)
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN \
_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END _Pragma("clang diagnostic pop")
#elif(__cplusplus >= 202002L) && defined(__GNUC__)
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN \
_Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END _Pragma("GCC diagnostic pop")
#else
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END
#endif
//! @endcond
namespace boost {
///
/// \brief This namespace includes implementations of the standard library functions and
/// classes such that they accept UTF-8 strings on Windows.
/// On other platforms (i.e. not on Windows) those functions and classes are just aliases
/// of the corresponding ones from the std namespace or behave like them.
///
namespace nowide {}
} // namespace boost
#endif

View File

@@ -0,0 +1,136 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_CONVERT_HPP_INCLUDED
#define BOOST_NOWIDE_CONVERT_HPP_INCLUDED
#include <boost/nowide/detail/is_string_container.hpp>
#include <boost/nowide/utf/convert.hpp>
#include <string>
namespace boost {
namespace nowide {
///
/// Convert wide string (UTF-16/32) in range [begin,end) to NULL terminated narrow string (UTF-8)
/// stored in \a output of size \a output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
inline char* narrow(char* output, size_t output_size, const wchar_t* begin, const wchar_t* end)
{
return utf::convert_buffer(output, output_size, begin, end);
}
///
/// Convert NULL terminated wide string (UTF-16/32) to NULL terminated narrow string (UTF-8)
/// stored in \a output of size \a output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
inline char* narrow(char* output, size_t output_size, const wchar_t* source)
{
return narrow(output, output_size, source, source + utf::strlen(source));
}
///
/// Convert narrow string (UTF-8) in range [begin,end) to NULL terminated wide string (UTF-16/32)
/// stored in \a output of size \a output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
inline wchar_t* widen(wchar_t* output, size_t output_size, const char* begin, const char* end)
{
return utf::convert_buffer(output, output_size, begin, end);
}
///
/// Convert NULL terminated narrow string (UTF-8) to NULL terminated wide string (UTF-16/32)
/// most output_size (including NULL)
///
/// If there is not enough room NULL is returned, else output is returned.
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
inline wchar_t* widen(wchar_t* output, size_t output_size, const char* source)
{
return widen(output, output_size, source, source + utf::strlen(source));
}
///
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
///
/// \param s Input string
/// \param count Number of characters to convert
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_wide_char<T_Char>>
inline std::string narrow(const T_Char* s, size_t count)
{
return utf::convert_string<char>(s, s + count);
}
///
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
///
/// \param s NULL terminated input string
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_wide_char<T_Char>>
inline std::string narrow(const T_Char* s)
{
return narrow(s, utf::strlen(s));
}
///
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
///
/// \param s Input string
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
template<typename StringOrStringView, typename = detail::requires_wide_string_container<StringOrStringView>>
inline std::string narrow(const StringOrStringView& s)
{
return utf::convert_string<char>(s.data(), s.data() + s.size());
}
///
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
///
/// \param s Input string
/// \param count Number of characters to convert
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_narrow_char<T_Char>>
inline std::wstring widen(const T_Char* s, size_t count)
{
return utf::convert_string<wchar_t>(s, s + count);
}
///
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
///
/// \param s NULL terminated input string
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
template<typename T_Char, typename = detail::requires_narrow_char<T_Char>>
inline std::wstring widen(const T_Char* s)
{
return widen(s, utf::strlen(s));
}
///
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
///
/// \param s Input string
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
template<typename StringOrStringView, typename = detail::requires_narrow_string_container<StringOrStringView>>
inline std::wstring widen(const StringOrStringView& s)
{
return utf::convert_string<wchar_t>(s.data(), s.data() + s.size());
}
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,46 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_CSTDIO_HPP_INCLUDED
#define BOOST_NOWIDE_CSTDIO_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#include <cstdio>
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::fopen;
using std::freopen;
using std::remove;
using std::rename;
#else
///
/// \brief Same as freopen but file_name and mode are UTF-8 strings
///
BOOST_NOWIDE_DECL FILE* freopen(const char* file_name, const char* mode, FILE* stream);
///
/// \brief Same as fopen but file_name and mode are UTF-8 strings
///
BOOST_NOWIDE_DECL FILE* fopen(const char* file_name, const char* mode);
///
/// \brief Same as rename but old_name and new_name are UTF-8 strings
///
BOOST_NOWIDE_DECL int rename(const char* old_name, const char* new_name);
///
/// \brief Same as rename but name is UTF-8 string
///
BOOST_NOWIDE_DECL int remove(const char* name);
#endif
namespace detail {
BOOST_NOWIDE_DECL FILE* wfopen(const wchar_t* filename, const wchar_t* mode);
}
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,71 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED
#define BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#if !defined(BOOST_WINDOWS)
#include <cstdlib>
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::getenv;
using std::system;
#else
///
/// \brief UTF-8 aware getenv. Returns 0 if the variable is not set.
///
/// The string pointed to shall not be modified by the program.
/// This function is thread-safe as long as no other thread modifies the host environment.
/// However subsequent calls to this function might overwrite the string pointed to.
///
/// Warning: The returned pointer might only be valid for as long as the calling thread is alive.
/// So avoid passing it across thread boundaries.
///
BOOST_NOWIDE_DECL char* getenv(const char* key);
///
/// Same as std::system but cmd is UTF-8.
///
BOOST_NOWIDE_DECL int system(const char* cmd);
#endif
///
/// \brief Set environment variable \a key to \a value
///
/// if overwrite is not 0, that the old value is always overwritten, otherwise,
/// if the variable exists it remains unchanged
///
/// \a key and \a value are UTF-8 on Windows
/// \return zero on success, else nonzero
///
BOOST_NOWIDE_DECL int setenv(const char* key, const char* value, int overwrite);
///
/// \brief Remove environment variable \a key
///
/// \a key is UTF-8 on Windows
/// \return zero on success, else nonzero
///
BOOST_NOWIDE_DECL int unsetenv(const char* key);
///
/// \brief Adds or changes an environment variable, \a string must be in format KEY=VALUE
///
/// \a string MAY become part of the environment, hence changes to the value MAY change
/// the environment. For portability it is hence recommended NOT to change it.
/// \a string is UTF-8 on Windows
/// \return zero on success, else nonzero
///
BOOST_NOWIDE_DECL int putenv(char* string);
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,24 @@
//
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_DETAIL_CONVERT_HPP_INCLUDED
#define BOOST_NOWIDE_DETAIL_CONVERT_HPP_INCLUDED
#include <boost/nowide/utf/convert.hpp>
// Legacy compatibility header only. Include <boost/nowide/utf/convert.hpp> instead
namespace boost {
namespace nowide {
namespace detail {
using boost::nowide::utf::convert_buffer;
using boost::nowide::utf::convert_string;
using boost::nowide::utf::strlen;
} // namespace detail
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,38 @@
//
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_DETAIL_IS_PATH_HPP_INCLUDED
#define BOOST_NOWIDE_DETAIL_IS_PATH_HPP_INCLUDED
#include <type_traits>
namespace boost {
namespace nowide {
namespace detail {
/// Trait to heuristically check for a *\::filesystem::path
/// Done by checking for make_preferred and filename member functions with correct signature
template<typename T>
struct is_path
{
template<typename U, U& (U::*)(), U (U::*)() const>
struct Check;
template<typename U>
static std::true_type test(Check<U, &U::make_preferred, &U::filename>*);
template<typename U>
static std::false_type test(...);
static constexpr bool value = decltype(test<T>(0))::value;
};
/// SFINAE trait/alias which resolves to Result if the Path is a *\::filesystem::path
template<typename Path, typename Result>
using enable_if_path_t = typename std::enable_if<is_path<Path>::value, Result>::type;
} // namespace detail
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,95 @@
//
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED
#define BOOST_NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED
#include <cstddef>
#include <type_traits>
namespace boost {
namespace nowide {
namespace detail {
template<class...>
struct make_void
{
typedef void type;
};
template<class... Ts>
using void_t = typename make_void<Ts...>::type;
template<typename T>
struct is_char_type : std::false_type
{};
template<>
struct is_char_type<char> : std::true_type
{};
template<>
struct is_char_type<wchar_t> : std::true_type
{};
template<>
struct is_char_type<char16_t> : std::true_type
{};
template<>
struct is_char_type<char32_t> : std::true_type
{};
#ifdef __cpp_char8_t
template<>
struct is_char_type<char8_t> : std::true_type
{};
#endif
template<typename T>
struct is_c_string : std::false_type
{};
template<typename T>
struct is_c_string<const T*> : is_char_type<T>
{};
template<typename T>
using const_data_result = decltype(std::declval<const T>().data());
/// Return the size of the char type returned by the data() member function
template<typename T>
using get_data_width =
std::integral_constant<std::size_t, sizeof(typename std::remove_pointer<const_data_result<T>>::type)>;
template<typename T>
using size_result = decltype(std::declval<T>().size());
/// Return true if the data() member function returns a pointer to a type of size 1
template<typename T>
using has_narrow_data = std::integral_constant<bool, (get_data_width<T>::value == 1)>;
/// Return true if T is a string container, e.g. std::basic_string, std::basic_string_view
/// Requires a static value `npos`, a member function `size()` returning an integral,
/// and a member function `data()` returning a C string
template<typename T, bool isNarrow, typename = void>
struct is_string_container : std::false_type
{};
// clang-format off
template<typename T, bool isNarrow>
struct is_string_container<T, isNarrow, void_t<decltype(T::npos), size_result<T>, const_data_result<T>>>
: std::integral_constant<bool,
std::is_integral<decltype(T::npos)>::value
&& std::is_integral<size_result<T>>::value
&& is_c_string<const_data_result<T>>::value
&& isNarrow == has_narrow_data<T>::value>
{};
// clang-format on
template<typename T>
using requires_narrow_string_container = typename std::enable_if<is_string_container<T, true>::value>::type;
template<typename T>
using requires_wide_string_container = typename std::enable_if<is_string_container<T, false>::value>::type;
template<typename T>
using requires_narrow_char = typename std::enable_if<sizeof(T) == 1 && is_char_type<T>::value>::type;
template<typename T>
using requires_wide_char = typename std::enable_if<(sizeof(T) > 1) && is_char_type<T>::value>::type;
} // namespace detail
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,22 @@
//
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_DETAIL_UTF_HPP_INCLUDED
#define BOOST_NOWIDE_DETAIL_UTF_HPP_INCLUDED
#include <boost/nowide/utf/utf.hpp>
// Legacy compatibility header only. Include <boost/nowide/utf/utf.hpp> instead
namespace boost {
namespace nowide {
namespace detail {
namespace utf = boost::nowide::utf;
} // namespace detail
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,546 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2019-2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
#define BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/detail/is_path.hpp>
#include <boost/nowide/stackstring.hpp>
#include <cassert>
#include <cstdio>
#include <ios>
#include <limits>
#include <locale>
#include <stdexcept>
#include <streambuf>
#else
#include <fstream>
#endif
namespace boost {
namespace nowide {
namespace detail {
/// Same as std::ftell but potentially with Large File Support
BOOST_NOWIDE_DECL std::streampos ftell(FILE* file);
/// Same as std::fseek but potentially with Large File Support
BOOST_NOWIDE_DECL int fseek(FILE* file, std::streamoff offset, int origin);
} // namespace detail
#if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT && !defined(BOOST_NOWIDE_DOXYGEN)
using std::basic_filebuf;
using std::filebuf;
#else // Windows
///
/// \brief This forward declaration defines the basic_filebuf type
/// which is used when #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT is set, e.g. on Windows.
///
/// It is implemented and specialized for CharType = char, it
/// implements std::filebuf over standard C I/O
///
template<typename CharType, typename Traits = std::char_traits<CharType>>
class basic_filebuf;
///
/// \brief This is the implementation of std::filebuf
/// which is used when #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT is set, e.g. on Windows.
///
/// It is implemented and specialized for CharType = char, it
/// implements std::filebuf over standard C I/O
///
template<>
class basic_filebuf<char> : public std::basic_streambuf<char>
{
using Traits = std::char_traits<char>;
public:
#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable : 4351) // new behavior : elements of array will be default initialized
#endif
///
/// Creates new filebuf
///
basic_filebuf() :
file_(nullptr), buffer_(nullptr), buffer_size_(BUFSIZ), owns_buffer_(false), unbuffered_read_(false),
last_char_(), mode_(std::ios_base::openmode(0))
{
setg(nullptr, nullptr, nullptr);
setp(nullptr, nullptr);
}
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
basic_filebuf(const basic_filebuf&) = delete;
basic_filebuf& operator=(const basic_filebuf&) = delete;
basic_filebuf(basic_filebuf&& other) noexcept : basic_filebuf()
{
swap(other);
}
basic_filebuf& operator=(basic_filebuf&& other) noexcept
{
close();
swap(other);
return *this;
}
void swap(basic_filebuf& rhs)
{
std::basic_streambuf<char>::swap(rhs);
using std::swap;
swap(file_, rhs.file_);
swap(buffer_, rhs.buffer_);
swap(buffer_size_, rhs.buffer_size_);
swap(owns_buffer_, rhs.owns_buffer_);
swap(unbuffered_read_, rhs.unbuffered_read_);
swap(last_char_[0], rhs.last_char_[0]);
swap(mode_, rhs.mode_);
// Fixup last_char references
if(pbase() == rhs.last_char_)
setp(last_char_, (pptr() == epptr()) ? last_char_ : last_char_ + 1);
if(eback() == rhs.last_char_)
setg(last_char_, (gptr() == rhs.last_char_) ? last_char_ : last_char_ + 1, last_char_ + 1);
if(rhs.pbase() == last_char_)
rhs.setp(rhs.last_char_, (rhs.pptr() == rhs.epptr()) ? rhs.last_char_ : rhs.last_char_ + 1);
if(rhs.eback() == last_char_)
{
rhs.setg(rhs.last_char_,
(rhs.gptr() == last_char_) ? rhs.last_char_ : rhs.last_char_ + 1,
rhs.last_char_ + 1);
}
}
virtual ~basic_filebuf()
{
close();
}
///
/// Same as std::filebuf::open but s is UTF-8 string
///
basic_filebuf* open(const std::string& s, std::ios_base::openmode mode)
{
return open(s.c_str(), mode);
}
///
/// Same as std::filebuf::open but s is UTF-8 string
///
basic_filebuf* open(const char* s, std::ios_base::openmode mode)
{
const wstackstring name(s);
return open(name.get(), mode);
}
/// Opens the file with the given name, see std::filebuf::open
basic_filebuf* open(const wchar_t* s, std::ios_base::openmode mode)
{
if(is_open())
return nullptr;
validate_cvt(this->getloc());
const bool ate = (mode & std::ios_base::ate) != 0;
if(ate)
mode &= ~std::ios_base::ate;
const wchar_t* smode = get_mode(mode);
if(!smode)
return nullptr;
file_ = detail::wfopen(s, smode);
if(!file_)
return nullptr;
if(ate && detail::fseek(file_, 0, SEEK_END) != 0)
{
close();
return nullptr;
}
mode_ = mode;
set_unbuffered_read();
return this;
}
template<typename Path>
detail::enable_if_path_t<Path, basic_filebuf*> open(const Path& file_name, std::ios_base::openmode mode)
{
return open(file_name.c_str(), mode);
}
///
/// Same as std::filebuf::close()
///
basic_filebuf* close()
{
if(!is_open())
return nullptr;
bool res = sync() == 0;
if(std::fclose(file_) != 0)
res = false;
file_ = nullptr;
mode_ = std::ios_base::openmode(0);
if(owns_buffer_)
{
delete[] buffer_;
buffer_ = nullptr;
owns_buffer_ = false;
}
setg(nullptr, nullptr, nullptr);
setp(nullptr, nullptr);
return res ? this : nullptr;
}
///
/// Same as std::filebuf::is_open()
///
bool is_open() const
{
return file_ != nullptr;
}
protected:
std::streambuf* setbuf(char* s, std::streamsize n) override
{
assert(n >= 0);
// Maximum compatibility: Discard all local buffers and use user-provided values
// Users should call sync() before or better use it before any IO is done or any file is opened
setg(nullptr, nullptr, nullptr);
setp(nullptr, nullptr);
if(owns_buffer_)
{
delete[] buffer_;
owns_buffer_ = false;
}
buffer_ = s;
buffer_size_ = (n >= 0) ? static_cast<size_t>(n) : 0;
set_unbuffered_read();
return this;
}
int sync() override
{
if(!file_)
return 0;
bool result;
if(pptr())
{
// Only flush if anything was written, otherwise behavior of fflush is undefined. I.e.:
// - Buffered mode: pptr was set to buffer_ and advanced
// - Unbuffered mode: pptr set to last_char_
const bool has_prev_write = pptr() != buffer_;
result = overflow() != EOF;
if(has_prev_write && std::fflush(file_) != 0)
result = false;
} else
result = stop_reading();
return result ? 0 : -1;
}
int overflow(int c = EOF) override
{
if(!(mode_ & (std::ios_base::out | std::ios_base::app)))
return EOF;
if(!stop_reading())
return EOF;
size_t n = pptr() - pbase();
if(n > 0)
{
if(std::fwrite(pbase(), 1, n, file_) != n)
return EOF;
assert(buffer_);
setp(buffer_, buffer_ + buffer_size_);
if(c != EOF)
{
*buffer_ = Traits::to_char_type(c);
pbump(1);
}
} else if(c != EOF)
{
if(buffer_size_ > 0)
{
make_buffer();
setp(buffer_, buffer_ + buffer_size_);
*buffer_ = Traits::to_char_type(c);
pbump(1);
} else if(std::fputc(c, file_) == EOF)
{
return EOF;
} else if(!pptr())
{
// Set to dummy value so we know we have written something
setp(last_char_, last_char_);
}
}
return Traits::not_eof(c);
}
std::streamsize xsputn(const char* s, std::streamsize n) override
{
// Only optimize when writing more than a buffer worth of data
if(n <= static_cast<std::streamsize>(buffer_size_))
return std::basic_streambuf<char>::xsputn(s, n);
if(!(mode_ & (std::ios_base::out | std::ios_base::app)) || !stop_reading())
return 0;
assert(n >= 0);
// First empty the remaining put area, if any
const char* const base = pbase();
const size_t num_buffered = pptr() - base;
if(num_buffered != 0)
{
const auto num_written = std::fwrite(base, 1, num_buffered, file_);
setp(const_cast<char*>(base + num_written), epptr()); // i.e. pbump(num_written)
if(num_written != num_buffered)
return 0; // Error writing buffered chars
}
// Then write directly to file
const auto num_written = std::fwrite(s, 1, static_cast<size_t>(n), file_);
if(num_written > 0u && base != last_char_)
setp(last_char_, last_char_); // Mark as "written" if not done yet
return num_written;
}
int underflow() override
{
if(!(mode_ & std::ios_base::in) || !stop_writing())
return EOF;
if(unbuffered_read_)
{
const int c = std::fgetc(file_);
if(c == EOF)
return EOF;
last_char_[0] = Traits::to_char_type(c);
setg(last_char_, last_char_, last_char_ + 1);
} else
{
make_buffer();
const size_t n = std::fread(buffer_, 1, buffer_size_, file_);
setg(buffer_, buffer_, buffer_ + n);
if(n == 0)
return EOF;
}
return Traits::to_int_type(*gptr());
}
std::streamsize xsgetn(char* s, std::streamsize n) override
{
// Only optimize when reading more than a buffer worth of data
if(n <= static_cast<std::streamsize>(unbuffered_read_ ? 1u : buffer_size_))
return std::basic_streambuf<char>::xsgetn(s, n);
if(!(mode_ & std::ios_base::in) || !stop_writing())
return 0;
assert(n >= 0);
std::streamsize num_copied = 0;
// First empty the remaining get area, if any
const auto num_buffered = egptr() - gptr();
if(num_buffered != 0)
{
const auto num_read = num_buffered > n ? n : num_buffered;
traits_type::copy(s, gptr(), static_cast<size_t>(num_read));
s += num_read;
n -= num_read;
num_copied = num_read;
setg(eback(), gptr() + num_read, egptr()); // i.e. gbump(num_read)
}
// Then read directly from file (loop as number of bytes read may be less than requested)
while(n > 0)
{
const auto num_read = std::fread(s, 1, static_cast<size_t>(n), file_);
if(num_read == 0) // EOF or error
break;
s += num_read;
n -= num_read;
num_copied += num_read;
}
return num_copied;
}
int pbackfail(int c = EOF) override
{
// For simplicity we only allow putting back into our read buffer
// So putting back more chars than we have read from the buffer will fail
if(gptr() > eback())
gbump(-1);
else
return EOF;
// Assign the new value if requested
if(c != EOF && *gptr() != Traits::to_char_type(c))
*gptr() = Traits::to_char_type(c);
return Traits::not_eof(c);
}
std::streampos seekoff(std::streamoff off,
std::ios_base::seekdir seekdir,
std::ios_base::openmode = std::ios_base::in | std::ios_base::out) override
{
if(!file_)
return EOF;
// Switching between input<->output requires a seek
// So do NOT optimize for seekoff(0, cur) as No-OP
// On some implementations a seek also flushes, so do a full sync
if(sync() != 0)
return EOF;
int whence;
switch(seekdir)
{
case std::ios_base::beg: whence = SEEK_SET; break;
case std::ios_base::cur: whence = SEEK_CUR; break;
case std::ios_base::end: whence = SEEK_END; break;
default: assert(false); return EOF;
}
if(detail::fseek(file_, off, whence) != 0)
return EOF;
return detail::ftell(file_);
}
std::streampos seekpos(std::streampos pos,
std::ios_base::openmode m = std::ios_base::in | std::ios_base::out) override
{
// Standard mandates "as-if fsetpos", but assume the effect is the same as fseek
return seekoff(pos, std::ios_base::beg, m);
}
void imbue(const std::locale& loc) override
{
validate_cvt(loc);
}
private:
void make_buffer()
{
if(buffer_)
return;
if(buffer_size_ > 0)
{
buffer_ = new char[buffer_size_];
owns_buffer_ = true;
}
}
void set_unbuffered_read()
{
// In text mode we cannot use buffering as we are required to know the (file) position of each
// char in the get area and to seek back in case of a sync to "put back" unread chars.
// However std::fseek with non-zero offsets is unsupported for text files and the (file) offset
// to seek back is unknown anyway due to newlines which may got converted.
unbuffered_read_ = !(mode_ & std::ios_base::binary) || buffer_size_ == 0u;
}
void validate_cvt(const std::locale& loc)
{
if(!std::use_facet<std::codecvt<char, char, std::mbstate_t>>(loc).always_noconv())
throw std::runtime_error("Converting codecvts are not supported");
}
/// Stop reading adjusting the file pointer if necessary
/// Postcondition: gptr() == nullptr
bool stop_reading()
{
if(!gptr())
return true;
const auto off = gptr() - egptr();
setg(nullptr, nullptr, nullptr);
if(!off)
return true;
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
// coverity[result_independent_of_operands]
if(off < std::numeric_limits<std::streamoff>::min())
return false;
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
return detail::fseek(file_, static_cast<std::streamoff>(off), SEEK_CUR) == 0;
}
/// Stop writing. If any bytes are to be written, writes them to file
/// Postcondition: pptr() == nullptr
bool stop_writing()
{
if(pptr())
{
const char* const base = pbase();
const size_t n = pptr() - base;
setp(nullptr, nullptr);
if(n && std::fwrite(base, 1, n, file_) != n)
return false;
}
return true;
}
static const wchar_t* get_mode(std::ios_base::openmode mode)
{
//
// done according to n2914 table 106 27.9.1.4
//
// note can't use switch case as overload operator can't be used
// in constant expression
if(mode == (std::ios_base::out))
return L"w";
if(mode == (std::ios_base::out | std::ios_base::app))
return L"a";
if(mode == (std::ios_base::app))
return L"a";
if(mode == (std::ios_base::out | std::ios_base::trunc))
return L"w";
if(mode == (std::ios_base::in))
return L"r";
if(mode == (std::ios_base::in | std::ios_base::out))
return L"r+";
if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
return L"w+";
if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::app))
return L"a+";
if(mode == (std::ios_base::in | std::ios_base::app))
return L"a+";
if(mode == (std::ios_base::binary | std::ios_base::out))
return L"wb";
if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::app))
return L"ab";
if(mode == (std::ios_base::binary | std::ios_base::app))
return L"ab";
if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc))
return L"wb";
if(mode == (std::ios_base::binary | std::ios_base::in))
return L"rb";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out))
return L"r+b";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
return L"w+b";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::app))
return L"a+b";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::app))
return L"a+b";
return nullptr;
}
FILE* file_;
char* buffer_;
size_t buffer_size_;
bool owns_buffer_;
bool unbuffered_read_; // True to read char by char
char last_char_[1];
std::ios::openmode mode_;
};
///
/// \brief Convenience typedef
///
using filebuf = basic_filebuf<char>;
/// Swap the basic_filebuf instances
template<typename CharType, typename Traits>
void swap(basic_filebuf<CharType, Traits>& lhs, basic_filebuf<CharType, Traits>& rhs)
{
lhs.swap(rhs);
}
#endif // windows
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,28 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED
#define BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED
#include <boost/nowide/utf8_codecvt.hpp>
#include <boost/filesystem/path.hpp>
namespace boost {
namespace nowide {
///
/// Install utf8_codecvt facet into boost::filesystem::path
/// such that all char strings are interpreted as UTF-8 strings
/// \return The previous imbued path locale.
///
inline std::locale nowide_filesystem()
{
std::locale tmp = std::locale(std::locale(), new boost::nowide::utf8_codecvt<wchar_t>());
return boost::filesystem::path::imbue(tmp);
}
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,359 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_FSTREAM_HPP_INCLUDED
#define BOOST_NOWIDE_FSTREAM_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#include <boost/nowide/detail/is_path.hpp>
#include <boost/nowide/filebuf.hpp>
#include <istream>
#include <ostream>
#include <utility>
namespace boost {
namespace nowide {
/// \cond INTERNAL
namespace detail {
// clang-format off
struct StreamTypeIn
{
static std::ios_base::openmode mode() { return std::ios_base::in; }
static std::ios_base::openmode mode_modifier() { return mode(); }
template<typename CharType, typename Traits>
struct stream_base{
using type = std::basic_istream<CharType, Traits>;
};
};
struct StreamTypeOut
{
static std::ios_base::openmode mode() { return std::ios_base::out; }
static std::ios_base::openmode mode_modifier() { return mode(); }
template<typename CharType, typename Traits>
struct stream_base{
using type = std::basic_ostream<CharType, Traits>;
};
};
struct StreamTypeInOut
{
static std::ios_base::openmode mode() { return std::ios_base::in | std::ios_base::out; }
static std::ios_base::openmode mode_modifier() { return std::ios_base::openmode(); }
template<typename CharType, typename Traits>
struct stream_base{
using type = std::basic_iostream<CharType, Traits>;
};
};
// clang-format on
/// Base class for all basic_*fstream classes
/// Contains basic_filebuf instance so its pointer can be used to construct basic_*stream
/// Provides common functions to reduce boilerplate code including inheriting from
/// the correct std::basic_[io]stream class and initializing it
/// \tparam T_StreamType One of StreamType* above.
/// Class used instead of value, because openmode::operator| may not be constexpr
/// \tparam FileBufType Discriminator to force a differing ABI if depending on the contained filebuf
template<typename CharType,
typename Traits,
typename T_StreamType,
int FileBufType = BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT>
class fstream_impl;
} // namespace detail
/// \endcond
///
/// \brief Same as std::basic_ifstream<char> but accepts UTF-8 strings under Windows
///
/// Affected by #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT and #BOOST_NOWIDE_USE_WCHAR_OVERLOADS
template<typename CharType, typename Traits = std::char_traits<CharType>>
class basic_ifstream : public detail::fstream_impl<CharType, Traits, detail::StreamTypeIn>
{
using fstream_impl = detail::fstream_impl<CharType, Traits, detail::StreamTypeIn>;
public:
basic_ifstream()
{}
explicit basic_ifstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::in)
{
open(file_name, mode);
}
#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS
explicit basic_ifstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in)
{
open(file_name, mode);
}
#endif
explicit basic_ifstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::in)
{
open(file_name, mode);
}
template<typename Path>
explicit basic_ifstream(const Path& file_name,
detail::enable_if_path_t<Path, std::ios_base::openmode> mode = std::ios_base::in)
{
open(file_name, mode);
}
using fstream_impl::open;
using fstream_impl::is_open;
using fstream_impl::close;
using fstream_impl::rdbuf;
using fstream_impl::swap;
basic_ifstream(const basic_ifstream&) = delete;
basic_ifstream& operator=(const basic_ifstream&) = delete;
basic_ifstream(basic_ifstream&& other) noexcept : fstream_impl(std::move(other))
{}
basic_ifstream& operator=(basic_ifstream&& rhs) noexcept
{
fstream_impl::operator=(std::move(rhs));
return *this;
}
};
///
/// \brief Same as std::basic_ofstream<char> but accepts UTF-8 strings under Windows
///
/// Affected by #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT and #BOOST_NOWIDE_USE_WCHAR_OVERLOADS
template<typename CharType, typename Traits = std::char_traits<CharType>>
class basic_ofstream : public detail::fstream_impl<CharType, Traits, detail::StreamTypeOut>
{
using fstream_impl = detail::fstream_impl<CharType, Traits, detail::StreamTypeOut>;
public:
basic_ofstream()
{}
explicit basic_ofstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::out)
{
open(file_name, mode);
}
#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS
explicit basic_ofstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::out)
{
open(file_name, mode);
}
#endif
explicit basic_ofstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::out)
{
open(file_name, mode);
}
template<typename Path>
explicit basic_ofstream(const Path& file_name,
detail::enable_if_path_t<Path, std::ios_base::openmode> mode = std::ios_base::out)
{
open(file_name, mode);
}
using fstream_impl::open;
using fstream_impl::is_open;
using fstream_impl::close;
using fstream_impl::rdbuf;
using fstream_impl::swap;
basic_ofstream(const basic_ofstream&) = delete;
basic_ofstream& operator=(const basic_ofstream&) = delete;
basic_ofstream(basic_ofstream&& other) noexcept : fstream_impl(std::move(other))
{}
basic_ofstream& operator=(basic_ofstream&& rhs)
{
fstream_impl::operator=(std::move(rhs));
return *this;
}
};
#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable : 4250) // <class> : inherits <method> via dominance
#endif
///
/// \brief Same as std::basic_fstream<char> but accepts UTF-8 strings under Windows
///
/// Affected by #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT and #BOOST_NOWIDE_USE_WCHAR_OVERLOADS
template<typename CharType, typename Traits = std::char_traits<CharType>>
class basic_fstream : public detail::fstream_impl<CharType, Traits, detail::StreamTypeInOut>
{
using fstream_impl = detail::fstream_impl<CharType, Traits, detail::StreamTypeInOut>;
public:
basic_fstream()
{}
explicit basic_fstream(const char* file_name,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
open(file_name, mode);
}
#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS
explicit basic_fstream(const wchar_t* file_name,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
open(file_name, mode);
}
#endif
explicit basic_fstream(const std::string& file_name,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
open(file_name, mode);
}
template<typename Path>
explicit basic_fstream(const Path& file_name,
detail::enable_if_path_t<Path, std::ios_base::openmode> mode = std::ios_base::in
| std::ios_base::out)
{
open(file_name, mode);
}
using fstream_impl::open;
using fstream_impl::is_open;
using fstream_impl::close;
using fstream_impl::rdbuf;
using fstream_impl::swap;
basic_fstream(const basic_fstream&) = delete;
basic_fstream& operator=(const basic_fstream&) = delete;
basic_fstream(basic_fstream&& other) noexcept : fstream_impl(std::move(other))
{}
basic_fstream& operator=(basic_fstream&& rhs)
{
fstream_impl::operator=(std::move(rhs));
return *this;
}
};
template<typename CharType, typename Traits>
void swap(basic_ifstream<CharType, Traits>& lhs, basic_ifstream<CharType, Traits>& rhs)
{
lhs.swap(rhs);
}
template<typename CharType, typename Traits>
void swap(basic_ofstream<CharType, Traits>& lhs, basic_ofstream<CharType, Traits>& rhs)
{
lhs.swap(rhs);
}
template<typename CharType, typename Traits>
void swap(basic_fstream<CharType, Traits>& lhs, basic_fstream<CharType, Traits>& rhs)
{
lhs.swap(rhs);
}
///
/// Same as std::filebuf but accepts UTF-8 strings under Windows
///
using filebuf = basic_filebuf<char>;
///
/// Same as std::ifstream but accepts UTF-8 strings under Windows
/// and *\::filesystem::path on all systems
///
using ifstream = basic_ifstream<char>;
///
/// Same as std::ofstream but accepts UTF-8 strings under Windows
/// and *\::filesystem::path on all systems
///
using ofstream = basic_ofstream<char>;
///
/// Same as std::fstream but accepts UTF-8 strings under Windows
/// and *\::filesystem::path on all systems
///
using fstream = basic_fstream<char>;
// Implementation
namespace detail {
/// Holds an instance of T
/// Required to make sure this is constructed first before passing it to sibling classes
template<typename T>
struct buf_holder
{
T buf_;
};
template<typename CharType, typename Traits, typename T_StreamType, int>
class fstream_impl : private buf_holder<basic_filebuf<CharType, Traits>>, // must be first due to init order
public T_StreamType::template stream_base<CharType, Traits>::type
{
using internal_buffer_type = basic_filebuf<CharType, Traits>;
using base_buf_holder = buf_holder<internal_buffer_type>;
using stream_base = typename T_StreamType::template stream_base<CharType, Traits>::type;
public:
using stream_base::setstate;
using stream_base::clear;
protected:
using base_buf_holder::buf_;
fstream_impl() : stream_base(&buf_)
{}
fstream_impl(const fstream_impl&) = delete;
fstream_impl& operator=(const fstream_impl&) = delete;
// coverity[exn_spec_violation]
fstream_impl(fstream_impl&& other) noexcept :
base_buf_holder(std::move(other)), stream_base(std::move(other))
{
this->set_rdbuf(rdbuf());
}
fstream_impl& operator=(fstream_impl&& rhs) noexcept
{
base_buf_holder::operator=(std::move(rhs));
stream_base::operator=(std::move(rhs));
return *this;
}
void swap(fstream_impl& other)
{
stream_base::swap(other);
rdbuf()->swap(*other.rdbuf());
}
void open(const std::string& file_name, std::ios_base::openmode mode = T_StreamType::mode())
{
open(file_name.c_str(), mode);
}
template<typename Path>
detail::enable_if_path_t<Path, void> open(const Path& file_name,
std::ios_base::openmode mode = T_StreamType::mode())
{
open(file_name.c_str(), mode);
}
void open(const char* file_name, std::ios_base::openmode mode = T_StreamType::mode())
{
if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier()))
setstate(std::ios_base::failbit);
else
clear();
}
#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS
void open(const wchar_t* file_name, std::ios_base::openmode mode = T_StreamType::mode())
{
if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier()))
setstate(std::ios_base::failbit);
else
clear();
}
#endif
bool is_open()
{
return rdbuf()->is_open();
}
bool is_open() const
{
return rdbuf()->is_open();
}
void close()
{
if(!rdbuf()->close())
setstate(std::ios_base::failbit);
}
internal_buffer_type* rdbuf() const
{
return const_cast<internal_buffer_type*>(&buf_);
}
};
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
} // namespace detail
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,111 @@
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020-2021 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_IOSTREAM_HPP_INCLUDED
#define BOOST_NOWIDE_IOSTREAM_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#ifdef BOOST_WINDOWS
#include <istream>
#include <memory>
#include <ostream>
#include <boost/config/abi_prefix.hpp> // must be the last #include
#else
#include <iostream>
#endif
#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable : 4251)
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::cout;
using std::cerr;
using std::cin;
using std::clog;
#else
/// \cond INTERNAL
namespace detail {
class console_output_buffer;
class console_input_buffer;
class BOOST_NOWIDE_DECL winconsole_ostream : public std::ostream
{
public:
enum class target_stream
{
output,
error,
log,
};
winconsole_ostream(target_stream target, bool isBuffered, winconsole_ostream* tieStream);
~winconsole_ostream();
private:
std::unique_ptr<console_output_buffer> d;
// Ensure the std streams are initialized and alive during the lifetime of this instance
std::ios_base::Init init_;
};
class BOOST_NOWIDE_DECL winconsole_istream : public std::istream
{
public:
explicit winconsole_istream(winconsole_ostream* tieStream);
~winconsole_istream();
private:
std::unique_ptr<console_input_buffer> d;
// Ensure the std streams are initialized and alive during the lifetime of this instance
std::ios_base::Init init_;
};
} // namespace detail
/// \endcond
///
/// \brief Same as std::cin, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL detail::winconsole_istream cin;
///
/// \brief Same as std::cout, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL detail::winconsole_ostream cout;
///
/// \brief Same as std::cerr, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL detail::winconsole_ostream cerr;
///
/// \brief Same as std::clog, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL detail::winconsole_ostream clog;
#endif
} // namespace nowide
} // namespace boost
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
#ifdef BOOST_WINDOWS
#include <boost/config/abi_suffix.hpp> // pops abi_prefix.hpp pragmas
#endif
#endif

View File

@@ -0,0 +1,109 @@
//
// Copyright (c) 2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_QUOTED_HPP_INCLUDED
#define BOOST_NOWIDE_QUOTED_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#include <boost/nowide/detail/is_path.hpp>
#include <boost/nowide/utf/convert.hpp>
#include <iomanip>
#include <istream>
#include <ostream>
#include <type_traits>
#if defined(__cpp_lib_quoted_string_io) && __cpp_lib_quoted_string_io >= 201304
namespace boost {
namespace nowide {
/// \cond INTERNAL
namespace detail {
template<class Path>
struct quoted;
template<typename T>
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
} // namespace detail
/// \endcond
/// \brief Allows insertion and extraction of `filesystem::path` into/from streams.
///
/// When used in an expression such as `out << quoted(path)`, where `out` is an output stream,
/// has the effect as-if `out << std::quoted(path.native())` was used.
///
/// When used in an expression like `in >> quoted(path)`, where `in` is an input stream,
/// has the effect as-if `in >> std::quoted(path.native())` was used if that would be valid.
/// To that effect a temporary string is used, which on success is assigned to `path`.
///
/// Will automatically convert between the streams `char_type` and `path::value_type` if necessary.
template<class Path>
#ifdef BOOST_NOWIDE_DOXYGEN
unspecified_type
#else
detail::enable_if_path_t<detail::remove_cvref_t<Path>, detail::quoted<Path&>>
#endif
quoted(Path& path)
{
return {path};
}
/// \cond INTERNAL
// Same but for const-refs and r-values
template<class Path>
detail::enable_if_path_t<detail::remove_cvref_t<Path>, detail::quoted<const Path&>> quoted(const Path& path)
{
return {path};
}
namespace detail {
template<typename CharOut,
typename CharIn,
typename = typename std::enable_if<!std::is_same<CharOut, CharIn>::value>::type>
std::basic_string<CharOut> maybe_convert_string(const std::basic_string<CharIn>& s)
{
return utf::convert_string<CharOut>(s);
}
template<typename Char>
const std::basic_string<Char>& maybe_convert_string(const std::basic_string<Char>& s)
{
return s;
}
template<typename T>
using requires_non_const =
typename std::enable_if<!std::is_const<typename std::remove_reference<T>::type>::value>::type;
template<class Path>
struct quoted
{
Path value;
template<typename CharType>
friend std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const quoted& path)
{
return out << std::quoted(maybe_convert_string<CharType>(path.value.native()));
}
template<typename CharType, class Path2 = Path, typename = requires_non_const<Path2>>
friend std::basic_istream<CharType>& operator>>(std::basic_istream<CharType>& in, const quoted& path)
{
std::basic_string<CharType> value;
using PlainPath = remove_cvref_t<Path>;
if(in >> std::quoted(value))
path.value = PlainPath(maybe_convert_string<typename PlainPath::value_type>(value));
return in;
}
};
} // namespace detail
/// \endcond
} // namespace nowide
} // namespace boost
#elif defined(BOOST_PRAGMA_MESSAGE)
BOOST_PRAGMA_MESSAGE("To use boost::nowide::quoted at least C++14 is required.")
#endif
#endif

View File

@@ -0,0 +1,18 @@
//
// Copyright (c) 2018 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_REPLACEMENT_HPP_INCLUDED
#define BOOST_NOWIDE_REPLACEMENT_HPP_INCLUDED
/// @file
/// \def BOOST_NOWIDE_REPLACEMENT_CHARACTER
/// Unicode character to be used to replace invalid UTF-8 sequences
#ifndef BOOST_NOWIDE_REPLACEMENT_CHARACTER
#define BOOST_NOWIDE_REPLACEMENT_CHARACTER 0xFFFD
#endif
#endif // boost/nowide/replacement.hpp

View File

@@ -0,0 +1,211 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_STACKSTRING_HPP_INCLUDED
#define BOOST_NOWIDE_STACKSTRING_HPP_INCLUDED
#include <boost/nowide/convert.hpp>
#include <boost/nowide/utf/utf.hpp>
#include <cassert>
#include <cstring>
namespace boost {
namespace nowide {
///
/// \brief A class that allows to create a temporary wide or narrow UTF strings from
/// wide or narrow UTF source.
///
/// It uses a stack buffer if the string is short enough
/// otherwise allocates a buffer on the heap.
///
/// Invalid UTF characters are replaced by the substitution character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
///
/// If a NULL pointer is passed to the constructor or convert method, NULL will be returned by c_str.
/// Similarly a default constructed stackstring will return NULL on calling c_str.
///
template<typename CharOut = wchar_t, typename CharIn = char, size_t BufferSize = 256>
class basic_stackstring
{
public:
/// Size of the stack buffer
static const size_t buffer_size = BufferSize;
/// Type of the output character (converted to)
using output_char = CharOut;
/// Type of the input character (converted from)
using input_char = CharIn;
/// Creates a NULL stackstring
basic_stackstring()
{
buffer_[0] = 0;
}
/// Convert the NULL terminated string input and store in internal buffer
/// If input is NULL, nothing will be stored
explicit basic_stackstring(const input_char* input)
{
convert(input);
}
/// Convert the sequence [begin, end) and store in internal buffer
/// If begin is NULL, nothing will be stored
basic_stackstring(const input_char* begin, const input_char* end)
{
convert(begin, end);
}
/// Copy construct from other
basic_stackstring(const basic_stackstring& other)
{
*this = other;
}
/// Copy assign from other
basic_stackstring& operator=(const basic_stackstring& other)
{
if(this != &other)
{
clear();
const size_t len = other.length();
if(other.uses_stack_memory())
data_ = buffer_;
else if(other.data_)
data_ = new output_char[len + 1];
else
{
data_ = nullptr;
return *this;
}
std::memcpy(data_, other.data_, sizeof(output_char) * (len + 1));
}
return *this;
}
~basic_stackstring()
{
clear();
}
/// Convert the NULL terminated string input and store in internal buffer
/// If input is NULL, the current buffer will be reset to NULL
output_char* convert(const input_char* input)
{
if(input)
return convert(input, input + utf::strlen(input));
clear();
return get();
}
/// Convert the sequence [begin, end) and store in internal buffer
/// If begin is NULL, the current buffer will be reset to NULL
output_char* convert(const input_char* begin, const input_char* end)
{
clear();
if(begin)
{
const size_t input_len = end - begin;
// Minimum size required: 1 output char per input char + trailing NULL
const size_t min_output_size = input_len + 1;
// If there is a chance the converted string fits on stack, try it
if(min_output_size <= buffer_size && utf::convert_buffer(buffer_, buffer_size, begin, end))
data_ = buffer_;
else
{
// Fallback: Allocate a buffer that is surely large enough on heap
// Max size: Every input char is transcoded to the output char with maximum with + trailing NULL
const size_t max_output_size = input_len * utf::utf_traits<output_char>::max_width + 1;
data_ = new output_char[max_output_size];
const bool success = utf::convert_buffer(data_, max_output_size, begin, end) == data_;
assert(success);
(void)success;
}
}
return get();
}
/// Return the converted, NULL-terminated string or NULL if no string was converted
output_char* get()
{
return data_;
}
/// Return the converted, NULL-terminated string or NULL if no string was converted
const output_char* get() const
{
return data_;
}
/// Reset the internal buffer to NULL
void clear()
{
if(!uses_stack_memory())
delete[] data_;
data_ = nullptr;
}
/// Swap lhs with rhs
friend void swap(basic_stackstring& lhs, basic_stackstring& rhs)
{
if(lhs.uses_stack_memory())
{
if(rhs.uses_stack_memory())
{
for(size_t i = 0; i < buffer_size; i++)
std::swap(lhs.buffer_[i], rhs.buffer_[i]);
} else
{
lhs.data_ = rhs.data_;
rhs.data_ = rhs.buffer_;
for(size_t i = 0; i < buffer_size; i++)
rhs.buffer_[i] = lhs.buffer_[i];
}
} else if(rhs.uses_stack_memory())
{
rhs.data_ = lhs.data_;
lhs.data_ = lhs.buffer_;
for(size_t i = 0; i < buffer_size; i++)
lhs.buffer_[i] = rhs.buffer_[i];
} else
std::swap(lhs.data_, rhs.data_);
}
protected:
/// True if the stack memory is used
bool uses_stack_memory() const
{
return data_ == buffer_;
}
/// Return the current length of the string excluding the NULL terminator
/// If NULL is stored returns NULL
size_t length() const
{
if(!data_)
return 0;
size_t len = 0;
while(data_[len])
len++;
return len;
}
private:
output_char buffer_[buffer_size];
output_char* data_ = nullptr;
}; // basic_stackstring
///
/// Convenience typedef
///
using wstackstring = basic_stackstring<wchar_t, char, 256>;
///
/// Convenience typedef
///
using stackstring = basic_stackstring<char, wchar_t, 256>;
///
/// Convenience typedef
///
using wshort_stackstring = basic_stackstring<wchar_t, char, 16>;
///
/// Convenience typedef
///
using short_stackstring = basic_stackstring<char, wchar_t, 16>;
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,69 @@
//
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_STAT_HPP_INCLUDED
#define BOOST_NOWIDE_STAT_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#include <sys/types.h>
// Include after sys/types.h
#include <sys/stat.h>
#if defined(__MINGW32__) && defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x0601
/// Forward declaration in case MinGW32 is used and __MSVCRT_VERSION__ is defined lower than 6.1
struct __stat64;
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
// Note: `using x = struct ::stat` causes a bogus warning in GCC < 11
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66159
typedef struct ::stat stat_t;
typedef struct ::stat posix_stat_t;
using ::stat;
#else
/// \brief Typedef for the file info structure.
/// Able to hold 64 bit file size and timestamps on Windows and usually also on other 64 Bit systems
/// This allows to write portable code with optional LFS support
typedef struct ::__stat64 stat_t;
/// \brief Typedef for the file info structure used in the POSIX stat call
/// Resolves to `struct _stat` on Windows and `struct stat` otherwise
/// This allows to write portable code using the default stat function
typedef struct ::_stat posix_stat_t;
/// \cond INTERNAL
namespace detail {
BOOST_NOWIDE_DECL int stat(const char* path, stat_t* buffer, size_t buffer_size);
BOOST_NOWIDE_DECL int stat(const char* path, posix_stat_t* buffer, size_t buffer_size);
} // namespace detail
/// \endcond
///
/// \brief UTF-8 aware stat function, returns 0 on success
///
/// Return information about a file from an UTF-8 encoded path
///
inline int stat(const char* path, stat_t* buffer)
{
return detail::stat(path, buffer, sizeof(*buffer));
}
///
/// \brief UTF-8 aware stat function, returns 0 on success
///
/// Return information about a file from an UTF-8 encoded path
///
inline int stat(const char* path, posix_stat_t* buffer)
{
return detail::stat(path, buffer, sizeof(*buffer));
}
#endif
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,108 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_UTF_CONVERT_HPP_INCLUDED
#define BOOST_NOWIDE_UTF_CONVERT_HPP_INCLUDED
#include <boost/nowide/detail/is_string_container.hpp>
#include <boost/nowide/replacement.hpp>
#include <boost/nowide/utf/utf.hpp>
#include <iterator>
#include <string>
namespace boost {
namespace nowide {
namespace utf {
/// Return the length of the given string in code units.
/// That is the number of elements of type Char until the first NULL character.
/// Equivalent to `std::strlen(s)` but can handle wide-strings
template<typename Char>
size_t strlen(const Char* s)
{
const Char* end = s;
while(*end)
end++;
return end - s;
}
/// Convert a buffer of UTF sequences in the range [source_begin, source_end)
/// from \a CharIn to \a CharOut to the output \a buffer of size \a buffer_size.
///
/// \return original buffer containing the NULL terminated string or NULL
///
/// If there is not enough room in the buffer NULL is returned, and the content of the buffer is undefined.
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
template<typename CharOut, typename CharIn>
CharOut*
convert_buffer(CharOut* buffer, size_t buffer_size, const CharIn* source_begin, const CharIn* source_end)
{
CharOut* rv = buffer;
if(buffer_size == 0)
return nullptr;
buffer_size--;
while(source_begin != source_end)
{
code_point c = utf_traits<CharIn>::decode(source_begin, source_end);
if(c == illegal || c == incomplete)
{
c = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
}
size_t width = utf_traits<CharOut>::width(c);
if(buffer_size < width)
{
rv = nullptr;
break;
}
buffer = utf_traits<CharOut>::encode(c, buffer);
buffer_size -= width;
}
*buffer++ = 0;
return rv;
}
/// Convert the UTF sequences in range [begin, end) from \a CharIn to \a CharOut
/// and return it as a string
///
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
/// \tparam CharOut Output character type
template<typename CharOut, typename CharIn>
std::basic_string<CharOut> convert_string(const CharIn* begin, const CharIn* end)
{
std::basic_string<CharOut> result;
result.reserve(end - begin);
using inserter_type = std::back_insert_iterator<std::basic_string<CharOut>>;
inserter_type inserter(result);
code_point c;
while(begin != end)
{
c = utf_traits<CharIn>::decode(begin, end);
if(c == illegal || c == incomplete)
{
c = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
}
utf_traits<CharOut>::encode(c, inserter);
}
return result;
}
/// Convert the UTF sequence in the input string from \a CharIn to \a CharOut
/// and return it as a string
///
/// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER
/// \tparam CharOut Output character type
template<typename CharOut, typename CharIn>
std::basic_string<CharOut> convert_string(const std::basic_string<CharIn>& s)
{
return convert_string<CharOut>(s.data(), s.data() + s.size());
}
} // namespace utf
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,454 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_UTF_HPP_INCLUDED
#define BOOST_NOWIDE_UTF_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#include <cstdint>
namespace boost {
namespace nowide {
///
/// \brief Namespace that holds basic operations on UTF encoded sequences
///
/// All functions defined in this namespace do not require linking with Boost.Nowide library.
/// Extracted from Boost.Locale
///
namespace utf {
///
/// \brief The integral type that can hold a Unicode code point
///
using code_point = uint32_t;
///
/// \brief Special constant that defines illegal code point
///
static const code_point illegal = 0xFFFFFFFFu;
///
/// \brief Special constant that defines incomplete code point
///
static const 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_NOWIDE_DOXYGEN
///
/// \brief UTF Traits class - functions to convert UTF sequences to and from Unicode code points
///
template<typename CharType, int size = sizeof(CharType)>
struct utf_traits
{
///
/// The type of the character
///
using char_type = CharType;
///
/// 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<typename Iterator>
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 const 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<typename Iterator>
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<typename Iterator>
static code_point decode_valid(Iterator& p);
};
#else
template<typename CharType, int size = sizeof(CharType)>
struct utf_traits;
template<typename CharType>
struct utf_traits<CharType, 1>
{
using char_type = CharType;
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 const 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<typename Iterator>
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_NOWIDE_FALLTHROUGH;
case 2:
if(BOOST_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
BOOST_NOWIDE_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
// - most compact representation
if(BOOST_UNLIKELY(!is_valid_codepoint(c)) || BOOST_UNLIKELY(width(c) != trail_size + 1))
{
p -= trail_size;
return illegal;
}
return c;
}
template<typename Iterator>
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<unsigned char>(*p++) & 0x3F); BOOST_NOWIDE_FALLTHROUGH;
case 2: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); BOOST_NOWIDE_FALLTHROUGH;
case 1: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F);
}
return c;
}
template<typename Iterator>
static Iterator encode(code_point value, Iterator out)
{
if(value <= 0x7F)
{
*out++ = static_cast<char_type>(value);
} else if(value <= 0x7FF)
{
*out++ = static_cast<char_type>((value >> 6) | 0xC0);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
} else if(BOOST_LIKELY(value <= 0xFFFF))
{
*out++ = static_cast<char_type>((value >> 12) | 0xE0);
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
} else
{
*out++ = static_cast<char_type>((value >> 18) | 0xF0);
*out++ = static_cast<char_type>(((value >> 12) & 0x3F) | 0x80);
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
}
return out;
}
}; // utf8
template<typename CharType>
struct utf_traits<CharType, 2>
{
using char_type = CharType;
// See RFC 2781
static bool is_single_codepoint(uint16_t x)
{
// Ranges [U+0000, 0+D7FF], [U+E000, U+FFFF] are numerically equal in UTF-16
return x <= 0xD7FF || x >= 0xE000;
}
static bool is_first_surrogate(uint16_t x)
{
// Range [U+D800, 0+DBFF]: High surrogate
return 0xD800 <= x && x <= 0xDBFF;
}
static bool is_second_surrogate(uint16_t x)
{
// Range [U+DC00, 0+DFFF]: Low surrogate
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;
}
/// Return true if c is trail code unit, always false for UTF-32
static bool is_trail(char_type c)
{
return is_second_surrogate(c);
}
/// Return 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<typename It>
static code_point decode(It& current, It last)
{
if(BOOST_UNLIKELY(current == last))
return incomplete;
uint16_t w1 = *current++;
if(BOOST_LIKELY(is_single_codepoint(w1)))
{
return w1;
}
// Now it's either a high or a low surrogate, the latter is invalid
if(w1 >= 0xDC00)
return illegal;
if(current == last)
return incomplete;
uint16_t w2 = *current++;
if(!is_second_surrogate(w2))
return illegal;
return combine_surrogate(w1, w2);
}
template<typename It>
static code_point decode_valid(It& current)
{
uint16_t w1 = *current++;
if(BOOST_LIKELY(is_single_codepoint(w1)))
{
return w1;
}
uint16_t w2 = *current++;
return combine_surrogate(w1, w2);
}
static const int max_width = 2;
static int width(code_point u) // LCOV_EXCL_LINE
{
return u >= 0x10000 ? 2 : 1;
}
template<typename It>
static It encode(code_point u, It out)
{
if(BOOST_LIKELY(u <= 0xFFFF))
{
*out++ = static_cast<char_type>(u);
} else
{
u -= 0x10000;
*out++ = static_cast<char_type>(0xD800 | (u >> 10));
*out++ = static_cast<char_type>(0xDC00 | (u & 0x3FF));
}
return out;
}
}; // utf16;
template<typename CharType>
struct utf_traits<CharType, 4>
{
using char_type = CharType;
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<typename It>
static code_point decode_valid(It& current)
{
return *current++;
}
template<typename It>
static code_point decode(It& current, It last)
{
if(BOOST_UNLIKELY(current == last))
return incomplete;
code_point c = *current++;
if(BOOST_UNLIKELY(!is_valid_codepoint(c)))
return illegal;
return c;
}
static const int max_width = 1;
static int width(code_point /*u*/)
{
return 1;
}
template<typename It>
static It encode(code_point u, It out)
{
*out++ = static_cast<char_type>(u);
return out;
}
}; // utf32
#endif
} // namespace utf
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,375 @@
//
// Copyright (c) 2015 Artyom Beilis (Tonkikh)
// Copyright (c) 2020 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_NOWIDE_UTF8_CODECVT_HPP_INCLUDED
#define BOOST_NOWIDE_UTF8_CODECVT_HPP_INCLUDED
#include <boost/nowide/replacement.hpp>
#include <boost/nowide/utf/utf.hpp>
#include <cassert>
#include <cstdint>
#include <locale>
namespace boost {
namespace nowide {
static_assert(sizeof(std::mbstate_t) >= 2, "mbstate_t is to small to store an UTF-16 codepoint");
namespace detail {
// Avoid including cstring for std::memcpy
inline void copy_uint16_t(void* dst, const void* src)
{
unsigned char* cdst = static_cast<unsigned char*>(dst);
const unsigned char* csrc = static_cast<const unsigned char*>(src);
cdst[0] = csrc[0];
cdst[1] = csrc[1];
}
inline std::uint16_t read_state(const std::mbstate_t& src)
{
std::uint16_t dst;
copy_uint16_t(&dst, &src);
return dst;
}
inline void write_state(std::mbstate_t& dst, const std::uint16_t src)
{
copy_uint16_t(&dst, &src);
}
} // namespace detail
/// std::codecvt implementation that converts between UTF-8 and UTF-16 or UTF-32
///
/// @tparam CharSize Determines the encoding: 2 for UTF-16, 4 for UTF-32
///
/// Invalid sequences are replaced by #BOOST_NOWIDE_REPLACEMENT_CHARACTER
/// A trailing incomplete sequence will result in a return value of std::codecvt::partial
template<typename CharType, int CharSize = sizeof(CharType)>
class utf8_codecvt;
BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN
/// Specialization for the UTF-8 <-> UTF-16 variant of the std::codecvt implementation
template<typename CharType>
class BOOST_SYMBOL_VISIBLE utf8_codecvt<CharType, 2> : public std::codecvt<CharType, char, std::mbstate_t>
{
public:
static_assert(sizeof(CharType) >= 2, "CharType must be able to store UTF16 code point");
utf8_codecvt(size_t refs = 0) : std::codecvt<CharType, char, std::mbstate_t>(refs)
{}
BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END
protected:
using uchar = CharType;
std::codecvt_base::result do_unshift(std::mbstate_t& s, char* from, char* /*to*/, char*& next) const override
{
if(detail::read_state(s) != 0)
return std::codecvt_base::error;
next = from;
return std::codecvt_base::ok;
}
int do_encoding() const noexcept override
{
return 0;
}
int do_max_length() const noexcept override
{
return 4;
}
bool do_always_noconv() const noexcept override
{
return false;
}
// LCOV_EXCL_START
int do_length(std::mbstate_t& std_state, const char* from, const char* from_end, size_t max) const override
{
// LCOV_EXCL_STOP
using utf16_traits = utf::utf_traits<uchar, 2>;
std::uint16_t state = detail::read_state(std_state);
const char* save_from = from;
if(state && max > 0)
{
max--;
state = 0;
}
while(max > 0 && from < from_end)
{
const char* prev_from = from;
std::uint32_t ch = utf::utf_traits<char>::decode(from, from_end);
if(ch == utf::illegal)
{
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
} else if(ch == utf::incomplete)
{
from = prev_from;
break;
}
// If we can't write the char, we have to save the low surrogate in state
if(BOOST_LIKELY(static_cast<size_t>(utf16_traits::width(ch)) <= max))
{
max -= utf16_traits::width(ch);
} else
{
static_assert(utf16_traits::max_width == 2, "Required for below");
std::uint16_t tmpOut[2]{};
utf16_traits::encode(ch, tmpOut);
state = tmpOut[1];
break;
}
}
detail::write_state(std_state, state);
return static_cast<int>(from - save_from);
}
std::codecvt_base::result do_in(std::mbstate_t& std_state, // LCOV_EXCL_LINE
const char* from,
const char* from_end,
const char*& from_next,
uchar* to,
uchar* to_end,
uchar*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
using utf16_traits = utf::utf_traits<uchar, 2>;
// mbstate_t is POD type and should be initialized to 0 (i.e. state = stateT())
// according to standard.
// We use it to store a low surrogate if it was not yet written, else state is 0
std::uint16_t state = detail::read_state(std_state);
// Write low surrogate if present
if(state && to < to_end)
{
*to++ = static_cast<CharType>(state);
state = 0;
}
while(to < to_end && from < from_end)
{
const char* from_saved = from;
uint32_t ch = utf::utf_traits<char>::decode(from, from_end);
if(ch == utf::illegal)
{
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
} else if(ch == utf::incomplete)
{
from = from_saved;
r = std::codecvt_base::partial;
break;
}
// If the encoded char fits, write directly, else safe the low surrogate in state
if(BOOST_LIKELY(utf16_traits::width(ch) <= to_end - to))
{
to = utf16_traits::encode(ch, to);
} else
{
static_assert(utf16_traits::max_width == 2, "Required for below");
std::uint16_t tmpOut[2]{};
utf16_traits::encode(ch, tmpOut);
*to++ = static_cast<CharType>(tmpOut[0]);
state = tmpOut[1];
break;
}
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && (from != from_end || state != 0))
r = std::codecvt_base::partial;
detail::write_state(std_state, state);
return r;
}
std::codecvt_base::result do_out(std::mbstate_t& std_state,
const uchar* from,
const uchar* from_end,
const uchar*& from_next,
char* to,
char* to_end,
char*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
using utf16_traits = utf::utf_traits<uchar, 2>;
// mbstate_t is POD type and should be initialized to 0
// (i.e. state = stateT()) according to standard.
// We use it to store the first observed surrogate pair, or 0 if there is none yet
std::uint16_t state = detail::read_state(std_state);
for(; to < to_end && from < from_end; ++from)
{
std::uint32_t ch = 0;
if(state != 0)
{
// We have a high surrogate, so now there should be a low surrogate
std::uint16_t w1 = state;
std::uint16_t w2 = *from;
if(BOOST_LIKELY(utf16_traits::is_trail(w2)))
{
ch = utf16_traits::combine_surrogate(w1, w2);
} else
{
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
}
} else
{
std::uint16_t w1 = *from;
if(BOOST_LIKELY(utf16_traits::is_single_codepoint(w1)))
{
ch = w1;
} else if(BOOST_LIKELY(utf16_traits::is_first_surrogate(w1)))
{
// Store into state and continue at next character
state = w1;
continue;
} else
{
// Neither a single codepoint nor a high surrogate so must be low surrogate.
// This is an error -> Replace character
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
}
}
assert(utf::is_valid_codepoint(ch)); // Any valid UTF16 sequence is a valid codepoint
int len = utf::utf_traits<char>::width(ch);
if(to_end - to < len)
{
r = std::codecvt_base::partial;
break;
}
to = utf::utf_traits<char>::encode(ch, to);
state = 0;
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && (from != from_end || state != 0))
r = std::codecvt_base::partial;
detail::write_state(std_state, state);
return r;
}
};
BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN
/// Specialization for the UTF-8 <-> UTF-32 variant of the std::codecvt implementation
template<typename CharType>
class BOOST_SYMBOL_VISIBLE utf8_codecvt<CharType, 4> : public std::codecvt<CharType, char, std::mbstate_t>
{
public:
utf8_codecvt(size_t refs = 0) : std::codecvt<CharType, char, std::mbstate_t>(refs)
{}
BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END
protected:
using uchar = CharType;
std::codecvt_base::result
do_unshift(std::mbstate_t& /*s*/, char* from, char* /*to*/, char*& next) const override
{
next = from;
return std::codecvt_base::noconv;
}
int do_encoding() const noexcept override
{
return 0;
}
int do_max_length() const noexcept override
{
return 4;
}
bool do_always_noconv() const noexcept override
{
return false;
}
int do_length(std::mbstate_t& /*state*/, const char* from, const char* from_end, size_t max) const override
{
const char* start_from = from;
while(max > 0 && from < from_end)
{
const char* save_from = from;
std::uint32_t ch = utf::utf_traits<char>::decode(from, from_end);
if(ch == utf::incomplete)
{
from = save_from;
break;
} else if(ch == utf::illegal)
{
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
}
max--;
}
return static_cast<int>(from - start_from);
}
std::codecvt_base::result do_in(std::mbstate_t& /*state*/,
const char* from,
const char* from_end,
const char*& from_next,
uchar* to,
uchar* to_end,
uchar*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
while(to < to_end && from < from_end)
{
const char* from_saved = from;
uint32_t ch = utf::utf_traits<char>::decode(from, from_end);
if(ch == utf::illegal)
{
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
} else if(ch == utf::incomplete)
{
r = std::codecvt_base::partial;
from = from_saved;
break;
}
*to++ = ch;
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && from != from_end)
r = std::codecvt_base::partial;
return r;
}
std::codecvt_base::result do_out(std::mbstate_t& /*std_state*/,
const uchar* from,
const uchar* from_end,
const uchar*& from_next,
char* to,
char* to_end,
char*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
while(to < to_end && from < from_end)
{
std::uint32_t ch = 0;
ch = *from;
if(!utf::is_valid_codepoint(ch))
{
ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
}
int len = utf::utf_traits<char>::width(ch);
if(to_end - to < len)
{
r = std::codecvt_base::partial;
break;
}
to = utf::utf_traits<char>::encode(ch, to);
from++;
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && from != from_end)
r = std::codecvt_base::partial;
return r;
}
};
} // namespace nowide
} // namespace boost
#endif

View File

@@ -0,0 +1,38 @@
//
// Copyright (c) 2012 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_NOWIDE_WINDOWS_HPP_INCLUDED
#define BOOST_NOWIDE_WINDOWS_HPP_INCLUDED
#ifdef BOOST_USE_WINDOWS_H
#include <windows.h>
// (Usually) included by windows.h
#include <shellapi.h>
#else
// When BOOST_USE_WINDOWS_H is not defined we declare the function prototypes to avoid including windows.h
extern "C" {
// From windows.h
__declspec(dllimport) wchar_t* __stdcall GetEnvironmentStringsW(void);
__declspec(dllimport) int __stdcall FreeEnvironmentStringsW(wchar_t*);
__declspec(dllimport) wchar_t* __stdcall GetCommandLineW(void);
__declspec(dllimport) unsigned long __stdcall GetLastError();
__declspec(dllimport) void* __stdcall LocalFree(void*);
__declspec(dllimport) int __stdcall SetEnvironmentVariableW(const wchar_t*, const wchar_t*);
__declspec(dllimport) unsigned long __stdcall GetEnvironmentVariableW(const wchar_t*, wchar_t*, unsigned long);
// From shellapi.h
__declspec(dllimport) wchar_t** __stdcall CommandLineToArgvW(const wchar_t*, int*);
}
#endif
#endif