diff --git a/.gitignore b/.gitignore index 3a3fea4411..1f77d34f37 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ Intermediate/ # Ignore cmake building directories build.*/ docs/ +out/ # Ignore NuGet artifacts .nuget/ Build_android/build/ diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 000516e1be..8092c72cad 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -13,6 +13,29 @@ set(CPPREST_VERSION_MAJOR 2) set(CPPREST_VERSION_MINOR 10) set(CPPREST_VERSION_REVISION 14) +# Note: when bumping CMake version to 3.3 or higher if(... IN_LIST ...) may be used +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 COMPILER_HAS_CXX_STD_17) +set(CPPREST_USE_STRING_VIEWS_DEFAULT_PER_CXX_STANDARD OFF) +if(COMPILER_HAS_CXX_STD_17 EQUAL "-1") + if(CPPREST_USE_STRING_VIEWS) + if(CMAKE_VERSION VERSION_LESS 3.8) + message(FATAL_ERROR "CPPREST_USE_STRING_VIEWS cannot be enabled. CMake 3.8 or higher required to support C++17.") + else() + message(FATAL_ERROR "CPPREST_USE_STRING_VIEWS cannot be enabled. Compiler (${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}) does not support C++17.") + endif() + endif() +elseif(CMAKE_CXX_STANDARD LESS 17) + if(CPPREST_USE_STRING_VIEWS) + message(FATAL_ERROR "CPPREST_USE_STRING_VIEWS cannot be enabled when C++ standard is less than 17") + endif() +elseif((NOT CMAKE_CXX_STANDARD GREATER 16.99) AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")) + if(CPPREST_USE_STRING_VIEWS) + message(FATAL_ERROR "CPPREST_USE_STRING_VIEWS cannot be enabled without setting C++ standard to 17 or greater for current configuration") # see compiler specific settings below + endif() +else() + set(CPPREST_USE_STRING_VIEWS_DEFAULT_PER_CXX_STANDARD ON) +endif() + enable_testing() set(WERROR ON CACHE BOOL "Treat Warnings as Errors.") @@ -20,6 +43,7 @@ set(CPPREST_EXCLUDE_WEBSOCKETS OFF CACHE BOOL "Exclude websockets functionality. set(CPPREST_EXCLUDE_COMPRESSION OFF CACHE BOOL "Exclude compression functionality.") set(CPPREST_EXCLUDE_BROTLI ON CACHE BOOL "Exclude Brotli compression functionality.") set(CPPREST_EXPORT_DIR cpprestsdk CACHE STRING "Directory to install CMake config files.") +option(CPPREST_USE_STRING_VIEWS "Use string_view types" ${CPPREST_USE_STRING_VIEWS_DEFAULT_PER_CXX_STANDARD}) set(CPPREST_INSTALL_HEADERS ON CACHE BOOL "Install header files.") set(CPPREST_INSTALL ON CACHE BOOL "Add install commands.") @@ -116,6 +140,11 @@ endif() set(WARNINGS) set(ANDROID_LIBS) +# Generic build settings +if(CPPREST_USE_STRING_VIEWS) + add_definitions(-DCPPREST_USE_STRING_VIEWS) +endif() + # Platform (not compiler) specific settings if(ANDROID) # These are used in the shared library case @@ -140,6 +169,15 @@ else() endif() # Compiler (not platform) specific settings +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(NOT CMAKE_CXX_STANDARD GREATER 0) + if(CPPREST_USE_STRING_VIEWS) + message(FATAL_ERROR "internal cpprestsdk cmake error: CPPREST_USE_STRING_VIEWS ON while C++ standard not set") + endif() + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + endif() +endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS) message("-- Setting clang options") @@ -157,10 +195,10 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -Wno-return-type-c-linkage -Wno-unneeded-internal-declaration") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") - set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++${CMAKE_CXX_STANDARD}") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message("-- Setting gcc options") @@ -168,7 +206,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(WARNINGS -Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) set(LD_FLAGS "${LD_FLAGS} -Wl,-z,defs") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -D_GLIBCXX_USE_SCHED_YIELD -D_GLIBCXX_USE_NANOSLEEP") endif() @@ -193,6 +231,13 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options(/permissive-) endif() endif() + + # Suppress C++17 deprecation warning in aiso per https://github.com/chriskohlhoff/asio/issues/290#issuecomment-371867040 + # MSVC has addressed issue in 16.0 per https://devblogs.microsoft.com/cppblog/cpp17-20-features-and-fixes-in-vs-2019/ + if(MSVC_VERSION GREATER 1912 AND MSVC_VERSION LESS 1920) + add_definitions(-D_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING) + endif() + else() message("-- Unknown compiler, success is doubtful.") message("CMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}") diff --git a/Release/include/cpprest/asyncrt_utils.h b/Release/include/cpprest/asyncrt_utils.h index 1b54f1634a..ebf0a9b7fa 100644 --- a/Release/include/cpprest/asyncrt_utils.h +++ b/Release/include/cpprest/asyncrt_utils.h @@ -14,25 +14,12 @@ #pragma once #include "cpprest/details/basic_types.h" -#include "pplx/pplxtasks.h" +#include "cpprest/string_utils.h" #include #include -#include -#include #include #include #include -#include - -#ifndef _WIN32 -#include -#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269 -/* Systems using glibc: xlocale.h has been removed from glibc 2.26 - The above include of locale.h is sufficient - Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */ -#include -#endif -#endif /// Various utilities for string conversions and date and time manipulation. namespace utility @@ -56,445 +43,8 @@ _ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds n _ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t& timespanString); } // namespace timespan -/// Functions for Unicode string conversions. -namespace conversions -{ -/// -/// Converts a UTF-16 string to a UTF-8 string. -/// -/// A two byte character UTF-16 string. -/// A single byte character UTF-8 string. -_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string& w); - -/// -/// Converts a UTF-8 string to a UTF-16 -/// -/// A single byte character UTF-8 string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string& s); - -/// -/// Converts a ASCII (us-ascii) string to a UTF-16 string. -/// -/// A single byte character us-ascii string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string& s); - -/// -/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string. -/// -/// A single byte character UTF-8 string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string& s); - -/// -/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. -/// -/// A single byte character UTF-8 string. -/// A single byte character UTF-8 string. -_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string& s); - -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A single byte character UTF-8 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s); -#else -inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); } -#endif - -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A two byte character UTF-16 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); } -#else -_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s); -#endif -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A single byte character UTF-8 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s); -#else -inline const utility::string_t& to_string_t(const std::string& s) { return s; } -#endif - -/// -/// Converts to a platform dependent Unicode string type. -/// -/// A two byte character UTF-16 string. -/// A platform dependent string type. -#ifdef _UTF16_STRINGS -inline const utility::string_t& to_string_t(const utf16string& s) { return s; } -#else -_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s); -#endif - -/// -/// Converts to a UTF-16 from string. -/// -/// A single byte character UTF-8 string. -/// A two byte character UTF-16 string. -_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string& value); - -/// -/// Converts to a UTF-16 from string. -/// -/// A two byte character UTF-16 string. -/// A two byte character UTF-16 string. -inline const utf16string& to_utf16string(const utf16string& value) { return value; } -/// -/// Converts to a UTF-16 from string. -/// -/// A two byte character UTF-16 string. -/// A two byte character UTF-16 string. -inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); } - -/// -/// Converts to a UTF-8 string. -/// -/// A single byte character UTF-8 string. -/// A single byte character UTF-8 string. -inline std::string&& to_utf8string(std::string&& value) { return std::move(value); } - -/// -/// Converts to a UTF-8 string. -/// -/// A single byte character UTF-8 string. -/// A single byte character UTF-8 string. -inline const std::string& to_utf8string(const std::string& value) { return value; } - -/// -/// Converts to a UTF-8 string. -/// -/// A two byte character UTF-16 string. -/// A single byte character UTF-8 string. -_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string& value); - -/// -/// Encode the given byte array into a base64 string -/// -_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); - -/// -/// Encode the given 8-byte integer into a base64 string -/// -_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); - -/// -/// Decode the given base64 string to a byte array -/// -_ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); - -template -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -utility::string_t print_string(const Source& val, const std::locale& loc = std::locale()) -{ - utility::ostringstream_t oss; - oss.imbue(loc); - oss << val; - if (oss.bad()) - { - throw std::bad_cast(); - } - return oss.str(); -} - -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -inline utility::string_t print_string(const utility::string_t& val) { return val; } - -namespace details -{ -#if defined(__ANDROID__) -template -inline std::string to_string(const T t) -{ - std::ostringstream os; - os.imbue(std::locale::classic()); - os << t; - return os.str(); -} -#endif - -template -inline utility::string_t to_string_t(const T t) -{ -#ifdef _UTF16_STRINGS - using std::to_wstring; - return to_wstring(t); -#else -#if !defined(__ANDROID__) - using std::to_string; -#endif - return to_string(t); -#endif -} - -template -utility::string_t print_string(const Source& val) -{ - utility::ostringstream_t oss; - oss.imbue(std::locale::classic()); - oss << val; - if (oss.bad()) - { - throw std::bad_cast(); - } - return oss.str(); -} - -inline const utility::string_t& print_string(const utility::string_t& val) { return val; } - -template -utf8string print_utf8string(const Source& val) -{ - return conversions::to_utf8string(print_string(val)); -} -inline const utf8string& print_utf8string(const utf8string& val) { return val; } - -template -Target scan_string(const utility::string_t& str) -{ - Target t; - utility::istringstream_t iss(str); - iss.imbue(std::locale::classic()); - iss >> t; - if (iss.bad()) - { - throw std::bad_cast(); - } - return t; -} - -inline const utility::string_t& scan_string(const utility::string_t& str) { return str; } -} // namespace details - -template -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale()) -{ - Target t; - utility::istringstream_t iss(str); - iss.imbue(loc); - iss >> t; - if (iss.bad()) - { - throw std::bad_cast(); - } - return t; -} - -CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " - "locale support is required.") -inline utility::string_t scan_string(const utility::string_t& str) { return str; } -} // namespace conversions - namespace details { -/// -/// Cross platform RAII container for setting thread local locale. -/// -class scoped_c_thread_locale -{ -public: - _ASYNCRTIMP scoped_c_thread_locale(); - _ASYNCRTIMP ~scoped_c_thread_locale(); - -#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 -#ifdef _WIN32 - typedef _locale_t xplat_locale; -#else - typedef locale_t xplat_locale; -#endif - - static _ASYNCRTIMP xplat_locale __cdecl c_locale(); -#endif -private: -#ifdef _WIN32 - std::string m_prevLocale; - int m_prevThreadSetting; -#elif !(defined(ANDROID) || defined(__ANDROID__)) - locale_t m_prevLocale; -#endif - scoped_c_thread_locale(const scoped_c_thread_locale&); - scoped_c_thread_locale& operator=(const scoped_c_thread_locale&); -}; - -/// -/// Our own implementation of alpha numeric instead of std::isalnum to avoid -/// taking global lock for performance reasons. -/// -inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT -{ // test if uch is an alnum character - // special casing char to avoid branches - // clang-format off - static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = { - /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */ - /* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */ - /* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */ - /* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - /* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */ - /* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 - /* non-ASCII values initialized to 0 */ - }; - // clang-format on - return (is_alnum_table[uch]); -} - -/// -/// Our own implementation of alpha numeric instead of std::isalnum to avoid -/// taking global lock for performance reasons. -/// -inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast(ch))); } - -/// -/// Our own implementation of alpha numeric instead of std::isalnum to avoid -/// taking global lock for performance reasons. -/// -template -inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT -{ - // assumes 'x' == L'x' for the ASCII range - typedef typename std::make_unsigned::type UElem; - const auto uch = static_cast(ch); - return (uch <= static_cast('z') && is_alnum(static_cast(uch))); -} - -/// -/// Our own implementation of whitespace test instead of std::isspace to avoid -/// taking global lock for performance reasons. -/// The following characters are considered whitespace: -/// 0x09 == Horizontal Tab -/// 0x0A == Line Feed -/// 0x0B == Vertical Tab -/// 0x0C == Form Feed -/// 0x0D == Carrage Return -/// 0x20 == Space -/// -template -inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT -{ - // assumes 'x' == L'x' for the ASCII range - typedef typename std::make_unsigned::type UElem; - const auto uch = static_cast(ch); - return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du); -} - -/// -/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates -/// and therefore not be compatible with Dev10. -/// -template -std::unique_ptr<_Type> make_unique() -{ - return std::unique_ptr<_Type>(new _Type()); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) -{ - return std::unique_ptr<_Type>( - new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) -{ - return std::unique_ptr<_Type>(new _Type( - std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), - std::forward<_Arg2>(arg2), - std::forward<_Arg3>(arg3), - std::forward<_Arg4>(arg4), - std::forward<_Arg5>(arg5))); -} - -template -std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6) -{ - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), - std::forward<_Arg2>(arg2), - std::forward<_Arg3>(arg3), - std::forward<_Arg4>(arg4), - std::forward<_Arg5>(arg5), - std::forward<_Arg6>(arg6))); -} - -/// -/// Cross platform utility function for performing case insensitive string equality comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if the strings are equivalent, false otherwise -_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT; - -/// -/// Cross platform utility function for performing case insensitive string equality comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if the strings are equivalent, false otherwise -_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT; - -/// -/// Cross platform utility function for performing case insensitive string less-than comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, -/// false. -_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT; - -/// -/// Cross platform utility function for performing case insensitive string less-than comparison. -/// -/// First string to compare. -/// Second strong to compare. -/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, -/// false. -_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT; - -/// -/// Convert a string to lowercase in place. -/// -/// The string to convert to lowercase. -_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT; - -/// -/// Convert a string to lowercase in place. -/// -/// The string to convert to lowercase. -_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT; #ifdef _WIN32 diff --git a/Release/include/cpprest/base64_utils.h b/Release/include/cpprest/base64_utils.h new file mode 100644 index 0000000000..a5c0823664 --- /dev/null +++ b/Release/include/cpprest/base64_utils.h @@ -0,0 +1,43 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Base64 utilities. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ + +#pragma once + +#include "cpprest/details/basic_types.h" +#include +#include + +/// Various utilities for string conversions and date and time manipulation. +namespace utility +{ +/// Functions for Unicode string conversions. +namespace conversions +{ +/// +/// Encode the given byte array into a base64 string +/// +_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); + +/// +/// Encode the given 8-byte integer into a base64 string +/// +_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); + +/// +/// Decode the given base64 string to a byte array +/// +_ASYNCRTIMP std::vector __cdecl from_base64(utility::string_view_t str); + +} // namespace conversions + +} // namespace utility diff --git a/Release/include/cpprest/details/basic_types.h b/Release/include/cpprest/details/basic_types.h index d2ceb87189..9a546665ea 100644 --- a/Release/include/cpprest/details/basic_types.h +++ b/Release/include/cpprest/details/basic_types.h @@ -18,6 +18,9 @@ #include #include #include +#if defined(CPPREST_USE_STRING_VIEWS) +#include +#endif #ifndef _WIN32 #ifndef __STDC_LIMIT_MACROS @@ -32,7 +35,7 @@ namespace utility { -#ifdef _WIN32 +#if defined(_WIN32) #define _UTF16_STRINGS #endif @@ -44,11 +47,22 @@ typedef uint64_t size64_t; typedef uint32_t HRESULT; // Needed for PPLX #endif +#if defined(CPPREST_USE_STRING_VIEWS) +typedef std::string_view nstring_view_t; +typedef std::wstring_view wstring_view_t; +template using string_view = std::basic_string_view; +#else +typedef const std::string & nstring_view_t; +typedef const std::wstring & wstring_view_t; +template using string_view = const std::basic_string &; +#endif + #ifdef _UTF16_STRINGS // // On Windows, all strings are wide // typedef wchar_t char_t; +typedef wstring_view_t string_view_t; typedef std::wstring string_t; #define _XPLATSTR(x) L##x typedef std::wostringstream ostringstream_t; @@ -66,6 +80,7 @@ typedef std::wstringstream stringstream_t; // On POSIX platforms, all strings are narrow // typedef char char_t; +typedef nstring_view_t string_view_t; typedef std::string string_t; #define _XPLATSTR(x) x typedef std::ostringstream ostringstream_t; @@ -90,6 +105,7 @@ typedef std::stringstream stringstream_t; } // namespace utility typedef char utf8char; +typedef utility::nstring_view_t utf8string_view; typedef std::string utf8string; typedef std::stringstream utf8stringstream; typedef std::ostringstream utf8ostringstream; @@ -97,8 +113,9 @@ typedef std::ostream utf8ostream; typedef std::istream utf8istream; typedef std::istringstream utf8istringstream; -#ifdef _UTF16_STRINGS +#if defined(_UTF16_STRINGS) || defined(_WIN32) typedef wchar_t utf16char; +typedef utility::wstring_view_t utf16string_view; typedef std::wstring utf16string; typedef std::wstringstream utf16stringstream; typedef std::wostringstream utf16ostringstream; @@ -107,6 +124,11 @@ typedef std::wistream utf16istream; typedef std::wistringstream utf16istringstream; #else typedef char16_t utf16char; +#if defined(CPPREST_USE_STRING_VIEWS) +typedef std::u16string_view utf16string_view; +#else +typedef const std::u16string & utf16string_view; +#endif typedef std::u16string utf16string; typedef std::basic_stringstream utf16stringstream; typedef std::basic_ostringstream utf16ostringstream; diff --git a/Release/include/cpprest/details/web_utilities.h b/Release/include/cpprest/details/web_utilities.h index 853d7614b1..441775cf62 100644 --- a/Release/include/cpprest/details/web_utilities.h +++ b/Release/include/cpprest/details/web_utilities.h @@ -12,6 +12,7 @@ #include "cpprest/asyncrt_utils.h" #include "cpprest/uri.h" +#include namespace web { @@ -31,7 +32,7 @@ class winrt_encryption { public: winrt_encryption() = default; - _ASYNCRTIMP winrt_encryption(const std::wstring& data); + _ASYNCRTIMP winrt_encryption(utility::string_view_t data); _ASYNCRTIMP plaintext_string decrypt() const; private: @@ -42,7 +43,7 @@ class win32_encryption { public: win32_encryption() = default; - _ASYNCRTIMP win32_encryption(const std::wstring& data); + _ASYNCRTIMP win32_encryption(::utility::string_view_t data); _ASYNCRTIMP ~win32_encryption(); _ASYNCRTIMP plaintext_string decrypt() const; @@ -72,7 +73,7 @@ class credentials /// /// User name as a string. /// Password as a string. - credentials(utility::string_t username, const utility::string_t& password) + credentials(utility::string_t username, utility::string_view_t password) : m_username(std::move(username)), m_password(password) { } diff --git a/Release/include/cpprest/filestream.h b/Release/include/cpprest/filestream.h index 1e4a0f278e..d71f9c20a7 100644 --- a/Release/include/cpprest/filestream.h +++ b/Release/include/cpprest/filestream.h @@ -17,6 +17,7 @@ #include "cpprest/astreambuf.h" #include "cpprest/details/fileio.h" +#include "cpprest/memory_utils.h" #include "cpprest/streams.h" #include @@ -712,7 +713,7 @@ class basic_file_buffer : public details::streambuf_state_manager<_CharType> #if !defined(__cplusplus_winrt) static pplx::task>> open( - const utility::string_t& _Filename, + utility::string_view_t _Filename, std::ios_base::openmode _Mode = std::ios_base::out, #ifdef _WIN32 int _Prot = (int)std::ios_base::_Openprot @@ -723,7 +724,7 @@ class basic_file_buffer : public details::streambuf_state_manager<_CharType> { auto result_tce = pplx::task_completion_event>>(); auto callback = new _filestream_callback_open(result_tce); - _open_fsb_str(callback, _Filename.c_str(), _Mode, _Prot); + _open_fsb_str(callback, _Filename.data(), _Mode, _Prot); return pplx::create_task(result_tce); } @@ -954,7 +955,7 @@ class file_buffer /// The opening mode of the file /// The file protection mode /// A task that returns an opened stream buffer on completion. - static pplx::task> open(const utility::string_t& file_name, + static pplx::task> open(utility::string_view_t file_name, std::ios_base::openmode mode = std::ios_base::out, #ifdef _WIN32 int prot = _SH_DENYRD @@ -1009,7 +1010,7 @@ class file_stream /// The opening mode of the file /// The file protection mode /// A task that returns an opened input stream on completion. - static pplx::task> open_istream(const utility::string_t& file_name, + static pplx::task> open_istream(utility::string_view_t file_name, std::ios_base::openmode mode = std::ios_base::in, #ifdef _WIN32 int prot = (int)std::ios_base::_Openprot @@ -1034,7 +1035,7 @@ class file_stream /// The opening mode of the file /// The file protection mode /// A task that returns an opened output stream on completion. - static pplx::task> open_ostream(const utility::string_t& file_name, + static pplx::task> open_ostream(utility::string_view_t file_name, std::ios_base::openmode mode = std::ios_base::out, #ifdef _WIN32 int prot = (int)std::ios_base::_Openprot diff --git a/Release/include/cpprest/http_compression.h b/Release/include/cpprest/http_compression.h index b0059a6419..6f5a44a079 100644 --- a/Release/include/cpprest/http_compression.h +++ b/Release/include/cpprest/http_compression.h @@ -12,6 +12,11 @@ ****/ #pragma once +#include "cpprest/details/basic_types.h" +#include "pplx/pplxtasks.h" +#include +#include + namespace web { namespace http diff --git a/Release/include/cpprest/http_listener.h b/Release/include/cpprest/http_listener.h index a5457c0135..7febdb9029 100644 --- a/Release/include/cpprest/http_listener.h +++ b/Release/include/cpprest/http_listener.h @@ -14,6 +14,7 @@ #define CASA_HTTP_LISTENER_H #include "cpprest/http_msg.h" +#include "cpprest/memory_utils.h" #include #include #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h index 50f05ef213..0ae159071a 100644 --- a/Release/include/cpprest/http_msg.h +++ b/Release/include/cpprest/http_msg.h @@ -196,7 +196,7 @@ class http_exception : public std::exception /// Error message string. http_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {} -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) /// /// Creates an http_exception with just a string message and no error code. /// @@ -225,8 +225,8 @@ class http_exception : public std::exception { } -#ifdef _WIN32 - /// +#if defined(_UTF16_STRINGS) + /// /// Creates an http_exception with from a error code using the current platform error category. /// /// Error code value. @@ -258,8 +258,8 @@ class http_exception : public std::exception { } -#ifdef _WIN32 - /// +#if defined(_UTF16_STRINGS) + /// /// Creates an http_exception with from a error code with a category, and a string message. /// /// Error code value. diff --git a/Release/include/cpprest/json.h b/Release/include/cpprest/json.h index 4549d1017c..174da35212 100644 --- a/Release/include/cpprest/json.h +++ b/Release/include/cpprest/json.h @@ -15,8 +15,11 @@ #ifndef CASA_JSON_H #define CASA_JSON_H -#include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" +#include "cpprest/memory_utils.h" +#include "cpprest/string_utils.h" + +#include #include #include #include @@ -275,10 +278,10 @@ class value /// static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) private: // Only used internally by JSON parser. - static _ASYNCRTIMP value __cdecl string(const std::string& value); + static _ASYNCRTIMP value __cdecl string(utility::nstring_view_t value); public: #endif @@ -391,7 +394,7 @@ class value /// Parses a string and construct a JSON value. /// /// The C++ value to create a JSON value from, a C++ STL double-byte string - _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value); + _ASYNCRTIMP static value __cdecl parse(utility::string_view_t value); /// /// Attempts to parse a string and construct a JSON value. @@ -399,7 +402,7 @@ class value /// The C++ value to create a JSON value from, a C++ STL double-byte string /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed - _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode); + _ASYNCRTIMP static value __cdecl parse(utility::string_view_t value, std::error_code& errorCode); /// /// Serializes the current JSON value to a C++ string. @@ -436,7 +439,7 @@ class value /// The stream that the JSON string representation should be written to. _ASYNCRTIMP void serialize(utility::ostream_t& stream) const; -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) /// /// Parses a JSON value from the contents of a single-byte (UTF8) stream. /// @@ -536,56 +539,56 @@ class value /// /// The name of the field /// True if the field exists, false otherwise. - bool has_field(const utility::string_t& key) const; + bool has_field(utility::string_view_t key) const; /// /// Tests for the presence of a number field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_number_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_number_field(utility::string_view_t key) const; /// /// Tests for the presence of an integer field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_integer_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_integer_field(utility::string_view_t key) const; /// /// Tests for the presence of a double field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_double_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_double_field(utility::string_view_t key) const; /// /// Tests for the presence of a boolean field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_boolean_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_boolean_field(utility::string_view_t key) const; /// /// Tests for the presence of a string field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_string_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_string_field(utility::string_view_t key) const; /// /// Tests for the presence of an array field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_array_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_array_field(utility::string_view_t key) const; /// /// Tests for the presence of an object field /// /// The name of the field /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_object_field(const utility::string_t& key) const; + _ASYNCRTIMP bool has_object_field(utility::string_view_t key) const; /// /// Accesses a field of a JSON object. @@ -606,7 +609,7 @@ class value /// Erases an element of a JSON object. Throws if the key doesn't exist. /// /// The key of the element to erase in the JSON object. - _ASYNCRTIMP void erase(const utility::string_t& key); + _ASYNCRTIMP void erase(utility::string_view_t key); /// /// Accesses an element of a JSON array. Throws when index out of bounds. @@ -627,26 +630,26 @@ class value /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value. - _ASYNCRTIMP json::value& at(const utility::string_t& key); + _ASYNCRTIMP json::value& at(utility::string_view_t key); /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value. - _ASYNCRTIMP const json::value& at(const utility::string_t& key) const; + _ASYNCRTIMP const json::value& at(utility::string_view_t key) const; /// /// Accesses a field of a JSON object. /// /// The name of the field /// A reference to the value kept in the field. - _ASYNCRTIMP value& operator[](const utility::string_t& key); + _ASYNCRTIMP value& operator[](utility::string_view_t key); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) private: // Only used internally by JSON parser - _ASYNCRTIMP value& operator[](const std::string& key) + _ASYNCRTIMP value& operator[](utility::nstring_view_t key) { // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid return operator[](utility::conversions::to_string_t(key)); @@ -677,7 +680,7 @@ class value template friend class web::json::details::JSON_Parser; -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) /// /// Writes the current JSON value as a double-byte string to a string instance. /// @@ -957,7 +960,7 @@ class object { if (!keep_order) { - sort(m_elements.begin(), m_elements.end(), compare_pairs); + std::sort(m_elements.begin(), m_elements.end(), compare_pairs); } } @@ -1047,7 +1050,7 @@ class object /// Deletes an element of the JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. - void erase(const utility::string_t& key) + void erase(utility::string_view_t key) { auto iter = find_by_key(key); if (iter == m_elements.end()) @@ -1063,7 +1066,7 @@ class object /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field. - json::value& at(const utility::string_t& key) + json::value& at(utility::string_view_t key) { auto iter = find_by_key(key); if (iter == m_elements.end()) @@ -1079,7 +1082,7 @@ class object /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field. - const json::value& at(const utility::string_t& key) const + const json::value& at(utility::string_view_t key) const { auto iter = find_by_key(key); if (iter == m_elements.end()) @@ -1096,7 +1099,7 @@ class object /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field, otherwise a newly created null value /// that will be stored for the given key. - json::value& operator[](const utility::string_t& key) + json::value& operator[](utility::string_view_t key) { auto iter = find_insert_location(key); @@ -1113,7 +1116,7 @@ class object /// /// The key of an element in the JSON object. /// A const iterator to the value kept in the field. - const_iterator find(const utility::string_t& key) const { return find_by_key(key); } + const_iterator find(utility::string_view_t key) const { return find_by_key(key); } /// /// Gets the number of elements of the object. @@ -1133,12 +1136,12 @@ class object { return p1.first < p2.first; } - static bool compare_with_key(const std::pair& p1, const utility::string_t& key) + static bool compare_with_key(const std::pair& p1, utility::string_view_t key) { return p1.first < key; } - storage_type::iterator find_insert_location(const utility::string_t& key) + storage_type::iterator find_insert_location(utility::string_view_t key) { if (m_keep_order) { @@ -1152,7 +1155,7 @@ class object } } - storage_type::const_iterator find_by_key(const utility::string_t& key) const + storage_type::const_iterator find_by_key(utility::string_view_t key) const { if (m_keep_order) { @@ -1171,7 +1174,7 @@ class object } } - storage_type::iterator find_by_key(const utility::string_t& key) + storage_type::iterator find_by_key(utility::string_view_t key) { auto iter = find_insert_location(key); if (iter != m_elements.end() && key != iter->first) @@ -1354,19 +1357,19 @@ class _Value public: virtual std::unique_ptr<_Value> _copy_value() = 0; - virtual bool has_field(const utility::string_t&) const { return false; } - virtual value get_field(const utility::string_t&) const { throw json_exception("not an object"); } + virtual bool has_field(utility::string_view_t) const { return false; } + virtual value get_field(utility::string_view_t) const { throw json_exception("not an object"); } virtual value get_element(array::size_type) const { throw json_exception("not an array"); } - virtual value& index(const utility::string_t&) { throw json_exception("not an object"); } + virtual value& index(utility::string_view_t) { throw json_exception("not an object"); } virtual value& index(array::size_type) { throw json_exception("not an array"); } - virtual const value& cnst_index(const utility::string_t&) const { throw json_exception("not an object"); } + virtual const value& cnst_index(utility::string_view_t) const { throw json_exception("not an object"); } virtual const value& cnst_index(array::size_type) const { throw json_exception("not an array"); } // Common function used for serialization to strings and streams. virtual void serialize_impl(std::string& str) const { format(str); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void serialize_impl(std::wstring& str) const { format(str); } #endif @@ -1400,7 +1403,7 @@ class _Value _Value() {} virtual void format(std::basic_string& stream) const { stream.append("null"); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void format(std::basic_string& stream) const { stream.append(L"null"); } #endif private: @@ -1438,7 +1441,7 @@ class _Number : public _Value protected: virtual void format(std::basic_string& stream) const; -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void format(std::basic_string& stream) const; #endif private: @@ -1462,7 +1465,7 @@ class _Boolean : public _Value protected: virtual void format(std::basic_string& stream) const { stream.append(m_value ? "true" : "false"); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void format(std::basic_string& stream) const { stream.append(m_value ? L"true" : L"false"); } #endif private: @@ -1479,7 +1482,7 @@ class _String : public _Value { } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) _String(std::string&& value) : m_string(utility::conversions::to_utf16string(std::move(value))) { m_has_escape_char = has_escape_chars(*this); @@ -1497,13 +1500,13 @@ class _String : public _Value virtual const utility::string_t& as_string() const; virtual void serialize_impl(std::string& str) const { serialize_impl_char_type(str); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); } #endif protected: virtual void format(std::basic_string& str) const; -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void format(std::basic_string& str) const; #endif @@ -1534,12 +1537,12 @@ class _String : public _Value }; template -_ASYNCRTIMP void append_escape_string(std::basic_string& str, const std::basic_string& escaped); +_ASYNCRTIMP void append_escape_string(std::basic_string& str, utility::string_view escaped); -void format_string(const utility::string_t& key, utility::string_t& str); +void format_string(utility::string_view_t key, utility::string_t& str); -#ifdef _WIN32 -void format_string(const utility::string_t& key, std::string& str); +#if defined(_UTF16_STRINGS) +void format_string(utility::string_view_t key, std::string& str); #endif class _Object : public _Value @@ -1556,9 +1559,9 @@ class _Object : public _Value virtual json::value::value_type type() const { return json::value::Object; } - virtual bool has_field(const utility::string_t&) const; + virtual bool has_field(utility::string_view_t) const; - virtual json::value& index(const utility::string_t& key); + virtual json::value& index(utility::string_view_t key); bool is_equal(const _Object* other) const { @@ -1573,7 +1576,7 @@ class _Object : public _Value str.reserve(get_reserve_size()); format(str); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void serialize_impl(std::wstring& str) const { // To avoid repeated allocations reserve some space all up front. @@ -1585,7 +1588,7 @@ class _Object : public _Value protected: virtual void format(std::basic_string& str) const { format_impl(str); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void format(std::basic_string& str) const { format_impl(str); } #endif @@ -1682,7 +1685,7 @@ class _Array : public _Value str.reserve(get_reserve_size()); format(str); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void serialize_impl(std::wstring& str) const { // To avoid repeated allocations reserve some space all up front. @@ -1694,7 +1697,7 @@ class _Array : public _Value protected: virtual void format(std::basic_string& str) const { format_impl(str); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) virtual void format(std::basic_string& str) const { format_impl(str); } #endif private: @@ -1749,7 +1752,7 @@ inline size_t json::value::size() const { return m_value->size(); } /// /// The name of the field /// True if the field exists, false otherwise. -inline bool json::value::has_field(const utility::string_t& key) const { return m_value->has_field(key); } +inline bool json::value::has_field(utility::string_view_t key) const { return m_value->has_field(key); } /// /// Access a field of a JSON object. diff --git a/Release/include/cpprest/memory_utils.h b/Release/include/cpprest/memory_utils.h new file mode 100644 index 0000000000..20fff2b250 --- /dev/null +++ b/Release/include/cpprest/memory_utils.h @@ -0,0 +1,85 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Memory utilities. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ + +#pragma once + +#include +#include + +/// Various utilities for string conversions and date and time manipulation. +namespace utility +{ + +namespace details +{ + +/// +/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates +/// and therefore not be compatible with Dev10. +/// +template +std::unique_ptr<_Type> make_unique() +{ + return std::unique_ptr<_Type>(new _Type()); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1) +{ + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) +{ + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) +{ + return std::unique_ptr<_Type>( + new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) +{ + return std::unique_ptr<_Type>(new _Type( + std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5) +{ + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), + std::forward<_Arg2>(arg2), + std::forward<_Arg3>(arg3), + std::forward<_Arg4>(arg4), + std::forward<_Arg5>(arg5))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6) +{ + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), + std::forward<_Arg2>(arg2), + std::forward<_Arg3>(arg3), + std::forward<_Arg4>(arg4), + std::forward<_Arg5>(arg5), + std::forward<_Arg6>(arg6))); +} + +} // namespace details + +} // namespace utility diff --git a/Release/include/cpprest/oauth1.h b/Release/include/cpprest/oauth1.h index dbc45bdd71..bf779d91ae 100644 --- a/Release/include/cpprest/oauth1.h +++ b/Release/include/cpprest/oauth1.h @@ -15,8 +15,10 @@ #ifndef CASA_OAUTH1_H #define CASA_OAUTH1_H +#include "cpprest/base64_utils.h" #include "cpprest/details/web_utilities.h" #include "cpprest/http_msg.h" +#include "cpprest/string_utils.h" namespace web { diff --git a/Release/include/cpprest/string_utils.h b/Release/include/cpprest/string_utils.h new file mode 100644 index 0000000000..7929f9e66b --- /dev/null +++ b/Release/include/cpprest/string_utils.h @@ -0,0 +1,441 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * String utilities. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ + +#pragma once + +#include "cpprest/details/basic_types.h" +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269 +/* Systems using glibc: xlocale.h has been removed from glibc 2.26 + The above include of locale.h is sufficient + Further details: https://sourceware.org/git/?p=glibc.git;a=commit;h=f0be25b6336db7492e47d2e8e72eb8af53b5506d */ +#include +#endif +#endif + +/// Various utilities for string conversions and date and time manipulation. +namespace utility +{ +/// Functions for Unicode string conversions. +namespace conversions +{ +/// +/// Converts a UTF-16 string to a UTF-8 string. +/// +/// A two byte character UTF-16 string. +/// A single byte character UTF-8 string. +_ASYNCRTIMP std::string __cdecl utf16_to_utf8(utf16string_view w); + +/// +/// Converts a UTF-8 string to a UTF-16 +/// +/// A single byte character UTF-8 string. +/// A two byte character UTF-16 string. +_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(utility::nstring_view_t s); + +/// +/// Converts a ASCII (us-ascii) string to a UTF-16 string. +/// +/// A single byte character us-ascii string. +/// A two byte character UTF-16 string. +_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(utility::nstring_view_t s); + +/// +/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string. +/// +/// A single byte character UTF-8 string. +/// A two byte character UTF-16 string. +_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(utility::nstring_view_t s); + +/// +/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. +/// +/// A single byte character UTF-8 string. +/// A single byte character UTF-8 string. +_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(utility::nstring_view_t s); + +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A single byte character UTF-8 string. +/// A platform dependent string type. +#ifdef _UTF16_STRINGS +_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s); +#else +inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); } +#endif + +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A two byte character UTF-16 string. +/// A platform dependent string type. +#ifdef _UTF16_STRINGS +inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); } +#else +_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s); +#endif +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A single byte character UTF-8 string. +/// A platform dependent string type. +#ifdef _UTF16_STRINGS +_ASYNCRTIMP utility::string_t __cdecl to_string_t(utility::nstring_view_t s); +#else +inline const utility::string_t& to_string_t(const std::string& s) { return s; } +#if CPPREST_USE_STRING_VIEWS +inline utility::string_view_t to_string_t(utility::nstring_view_t s) { return s; } +#endif +#endif + +#if CPPREST_USE_STRING_VIEWS +// Provide precise overload for string literals to resolve ambiguity of other overloads. +template +inline auto to_string_t(const CharType * s) { return to_string_t(std::basic_string_view(s)); } +#endif + +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A two byte character UTF-16 string. +/// A platform dependent string type. +#ifdef _UTF16_STRINGS +inline const utility::string_t& to_string_t(const utf16string& s) { return s; } +#if CPPREST_USE_STRING_VIEWS +inline utf16string_view to_string_t(utf16string_view s) { return s; } +#endif +#else +_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string_view s); +#endif + +/// +/// Converts to a UTF-16 from string. +/// +/// A single byte character UTF-8 string. +/// A two byte character UTF-16 string. +_ASYNCRTIMP utf16string __cdecl to_utf16string(utility::nstring_view_t value); + +/// +/// Converts to a UTF-16 from string. +/// +/// A two byte character UTF-16 string. +/// A two byte character UTF-16 string. +inline const utf16string& to_utf16string(const utf16string& value) { return value; } +#if CPPREST_USE_STRING_VIEWS +inline utf16string_view to_utf16string(utf16string_view value) { return value; } +#endif +/// +/// Converts to a UTF-16 from string. +/// +/// A two byte character UTF-16 string. +/// A two byte character UTF-16 string. +inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); } + +#if CPPREST_USE_STRING_VIEWS +// Provide precise overload for string literals to resolve ambiguity of other overloads. +template +inline auto to_utf16string(const CharType * s) { return to_utf16string(std::basic_string_view(s)); } +#endif + +/// +/// Converts to a UTF-8 string. +/// +/// A single byte character UTF-8 string. +/// A single byte character UTF-8 string. +inline std::string&& to_utf8string(std::string&& value) { return std::move(value); } + +/// +/// Converts to a UTF-8 string. +/// +/// A single byte character UTF-8 string. +/// A single byte character UTF-8 string. +inline const std::string& to_utf8string(const std::string& value) { return value; } +#if CPPREST_USE_STRING_VIEWS +inline utility::nstring_view_t to_utf8string(utility::nstring_view_t value) { return value; } +#endif + +/// +/// Converts to a UTF-8 string. +/// +/// A two byte character UTF-16 string. +/// A single byte character UTF-8 string. +_ASYNCRTIMP std::string __cdecl to_utf8string(utf16string_view value); + +#if CPPREST_USE_STRING_VIEWS +// Provide precise overload for string literals to resolve ambiguity of other overloads. +template +inline auto to_utf8string(const CharType * s) { return to_utf8string(std::basic_string_view(s)); } +#endif + +template +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +utility::string_t print_string(const Source& val, const std::locale& loc = std::locale()) +{ + utility::ostringstream_t oss; + oss.imbue(loc); + oss << val; + if (oss.bad()) + { + throw std::bad_cast(); + } + return oss.str(); +} + +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +inline utility::string_t print_string(const utility::string_t& val) { return val; } + +namespace details +{ +#if defined(__ANDROID__) +template +inline std::string to_string(const T t) +{ + std::ostringstream os; + os.imbue(std::locale::classic()); + os << t; + return os.str(); +} +#endif + +template +inline utility::string_t to_string_t(const T t) +{ +#ifdef _UTF16_STRINGS + using std::to_wstring; + return to_wstring(t); +#else +#if !defined(__ANDROID__) + using std::to_string; +#endif + return to_string(t); +#endif +} + +template +utility::string_t print_string(const Source& val) +{ + utility::ostringstream_t oss; + oss.imbue(std::locale::classic()); + oss << val; + if (oss.bad()) + { + throw std::bad_cast(); + } + return oss.str(); +} + +inline const utility::string_t& print_string(const utility::string_t& val) { return val; } +#if CPPREST_USE_STRING_VIEWS +inline utility::string_view_t print_string(utility::string_view_t val) { return val; } +#endif + +template +utf8string print_utf8string(const Source& val) +{ + return conversions::to_utf8string(print_string(val)); +} +inline const utf8string& print_utf8string(const utf8string& val) { return val; } + +template +Target scan_string(const utility::string_t& str) +{ + Target t; + utility::istringstream_t iss(str); + iss.imbue(std::locale::classic()); + iss >> t; + if (iss.bad()) + { + throw std::bad_cast(); + } + return t; +} + +inline const utility::string_t& scan_string(const utility::string_t& str) { return str; } +} // namespace details + +template +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale()) +{ + Target t; + utility::istringstream_t iss(str); + iss.imbue(loc); + iss >> t; + if (iss.bad()) + { + throw std::bad_cast(); + } + return t; +} + +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +inline utility::string_t scan_string(const utility::string_t& str) { return str; } +} // namespace conversions + +namespace details +{ +/// +/// Cross platform RAII container for setting thread local locale. +/// +class scoped_c_thread_locale +{ +public: + _ASYNCRTIMP scoped_c_thread_locale(); + _ASYNCRTIMP ~scoped_c_thread_locale(); + +#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 +#ifdef _WIN32 + typedef _locale_t xplat_locale; +#else + typedef locale_t xplat_locale; +#endif + + static _ASYNCRTIMP xplat_locale __cdecl c_locale(); +#endif +private: +#ifdef _WIN32 + std::string m_prevLocale; + int m_prevThreadSetting; +#elif !(defined(ANDROID) || defined(__ANDROID__)) + locale_t m_prevLocale; +#endif + scoped_c_thread_locale(const scoped_c_thread_locale&); + scoped_c_thread_locale& operator=(const scoped_c_thread_locale&); +}; + +/// +/// Our own implementation of alpha numeric instead of std::isalnum to avoid +/// taking global lock for performance reasons. +/// +inline bool __cdecl is_alnum(const unsigned char uch) CPPREST_NOEXCEPT +{ // test if uch is an alnum character + // special casing char to avoid branches + // clang-format off + static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = { + /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */ + /* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */ + /* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */ + /* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + /* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */ + /* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 + /* non-ASCII values initialized to 0 */ + }; + // clang-format on + return (is_alnum_table[uch]); +} + +/// +/// Our own implementation of alpha numeric instead of std::isalnum to avoid +/// taking global lock for performance reasons. +/// +inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast(ch))); } + +/// +/// Our own implementation of alpha numeric instead of std::isalnum to avoid +/// taking global lock for performance reasons. +/// +template +inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT +{ + // assumes 'x' == L'x' for the ASCII range + typedef typename std::make_unsigned::type UElem; + const auto uch = static_cast(ch); + return (uch <= static_cast('z') && is_alnum(static_cast(uch))); +} + +/// +/// Our own implementation of whitespace test instead of std::isspace to avoid +/// taking global lock for performance reasons. +/// The following characters are considered whitespace: +/// 0x09 == Horizontal Tab +/// 0x0A == Line Feed +/// 0x0B == Vertical Tab +/// 0x0C == Form Feed +/// 0x0D == Carrage Return +/// 0x20 == Space +/// +template +inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT +{ + // assumes 'x' == L'x' for the ASCII range + typedef typename std::make_unsigned::type UElem; + const auto uch = static_cast(ch); + return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du); +} + + +/// +/// Cross platform utility function for performing case insensitive string equality comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if the strings are equivalent, false otherwise +_ASYNCRTIMP bool __cdecl str_iequal(utility::nstring_view_t left, utility::nstring_view_t right) CPPREST_NOEXCEPT; + +/// +/// Cross platform utility function for performing case insensitive string equality comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if the strings are equivalent, false otherwise +_ASYNCRTIMP bool __cdecl str_iequal(utility::wstring_view_t left, utility::wstring_view_t right) CPPREST_NOEXCEPT; + +/// +/// Cross platform utility function for performing case insensitive string less-than comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, +/// false. +_ASYNCRTIMP bool __cdecl str_iless(utility::nstring_view_t left, utility::nstring_view_t right) CPPREST_NOEXCEPT; + +/// +/// Cross platform utility function for performing case insensitive string less-than comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, +/// false. +_ASYNCRTIMP bool __cdecl str_iless(utility::wstring_view_t left, utility::wstring_view_t right) CPPREST_NOEXCEPT; + +/// +/// Convert a string to lowercase in place. +/// +/// The string to convert to lowercase. +_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT; + +/// +/// Convert a string to lowercase in place. +/// +/// The string to convert to lowercase. +_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT; + +} // namespace details + +} // namespace utility diff --git a/Release/include/pplx/threadpool.h b/Release/include/pplx/threadpool.h index b297ff6bc8..bd77a27038 100644 --- a/Release/include/pplx/threadpool.h +++ b/Release/include/pplx/threadpool.h @@ -29,6 +29,7 @@ #endif #include "cpprest/details/cpprest_compat.h" +#include namespace crossplat { diff --git a/Release/samples/Oauth1Client/Oauth1Client.cpp b/Release/samples/Oauth1Client/Oauth1Client.cpp index dec9b342b5..ba96ad20bf 100644 --- a/Release/samples/Oauth1Client/Oauth1Client.cpp +++ b/Release/samples/Oauth1Client/Oauth1Client.cpp @@ -62,7 +62,7 @@ static void open_browser(utility::string_t auth_uri) { #if defined(_WIN32) && !defined(__cplusplus_winrt) // NOTE: Windows desktop only. - auto r = ShellExecuteA(NULL, "open", conversions::utf16_to_utf8(auth_uri).c_str(), NULL, NULL, SW_SHOWNORMAL); + auto r = ShellExecuteA(NULL, "open", conversions::to_utf8string(auth_uri).c_str(), NULL, NULL, SW_SHOWNORMAL); #elif defined(__APPLE__) // NOTE: OS X only. string_t browser_cmd(U("open \"") + auth_uri + U("\"")); diff --git a/Release/samples/Oauth2Client/Oauth2Client.cpp b/Release/samples/Oauth2Client/Oauth2Client.cpp index 19072cf6c6..e2e5c275ff 100644 --- a/Release/samples/Oauth2Client/Oauth2Client.cpp +++ b/Release/samples/Oauth2Client/Oauth2Client.cpp @@ -65,7 +65,7 @@ static void open_browser(utility::string_t auth_uri) { #if defined(_WIN32) && !defined(__cplusplus_winrt) // NOTE: Windows desktop only. - auto r = ShellExecuteA(NULL, "open", conversions::utf16_to_utf8(auth_uri).c_str(), NULL, NULL, SW_SHOWNORMAL); + auto r = ShellExecuteA(NULL, "open", conversions::to_utf8string(auth_uri).c_str(), NULL, NULL, SW_SHOWNORMAL); #elif defined(__APPLE__) // NOTE: OS X only. string_t browser_cmd(U("open \"") + auth_uri + U("\"")); diff --git a/Release/samples/SearchFile/searchfile.cpp b/Release/samples/SearchFile/searchfile.cpp index dfcc38bc2c..480fe96752 100644 --- a/Release/samples/SearchFile/searchfile.cpp +++ b/Release/samples/SearchFile/searchfile.cpp @@ -86,8 +86,8 @@ class type_parser /// Function to create in data from a file and search for a given string writing all lines containing the string to /// memory_buffer. /// -static pplx::task find_matches_in_file(const string_t& fileName, - const std::string& searchString, +static pplx::task find_matches_in_file(utility::string_view_t fileName, + utility::nstring_view_t searchString, basic_ostream results) { return file_stream::open_istream(fileName).then([=](basic_istream inFile) { @@ -130,7 +130,7 @@ static pplx::task find_matches_in_file(const string_t& fileName, /// /// Function to write out results from matched_lines type to file /// -static pplx::task write_matches_to_file(const string_t& fileName, matched_lines results) +static pplx::task write_matches_to_file(utility::string_view_t fileName, matched_lines results) { // Create a shared pointer to the matched_lines structure to copying repeatedly. auto sharedResults = std::make_shared(std::move(results)); @@ -162,9 +162,9 @@ int main(int argc, char* args[]) printf("Usage: SearchFile.exe input_file search_string output_file\n"); return -1; } - const string_t inFileName = args[1]; - const std::string searchString = utility::conversions::to_utf8string(args[2]); - const string_t outFileName = args[3]; + const auto inFileName = utility::conversions::to_string_t(args[1]); + const auto searchString = utility::conversions::to_utf8string(args[2]); + const auto outFileName = utility::conversions::to_string_t(args[3]); producer_consumer_buffer lineResultsBuffer; // Find all matches in file. diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index 1c40257c47..af31a9f2c4 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -36,6 +36,7 @@ set(SOURCES uri/uri_builder.cpp utilities/asyncrt_utils.cpp utilities/base64.cpp + utilities/string_utils.cpp utilities/web_utilities.cpp ) @@ -47,6 +48,10 @@ target_include_directories(cpprest pch ) +if(CPPREST_USE_STRING_VIEWS) + target_compile_features(cpprest PUBLIC cxx_std_17) +endif() + ## Sub-components # Websockets component if(CPPREST_WEBSOCKETS_IMPL STREQUAL "none") diff --git a/Release/src/http/oauth/oauth1.cpp b/Release/src/http/oauth/oauth1.cpp index b313cfcdb3..4db2d6e80c 100644 --- a/Release/src/http/oauth/oauth1.cpp +++ b/Release/src/http/oauth/oauth1.cpp @@ -62,8 +62,8 @@ std::vector oauth1_config::_hmac_sha1(const utility::string_t& ke DWORD hash_len = 0; ULONG result_len = 0; - const auto& key_c = conversions::utf16_to_utf8(key); - const auto& data_c = conversions::utf16_to_utf8(data); + const auto& key_c = conversions::to_utf8string(key); + const auto& data_c = conversions::to_utf8string(data); status = BCryptOpenAlgorithmProvider(&alg_handle, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (!NT_SUCCESS(status)) diff --git a/Release/src/json/json.cpp b/Release/src/json/json.cpp index f1f0865d87..70e227f197 100644 --- a/Release/src/json/json.cpp +++ b/Release/src/json/json.cpp @@ -195,8 +195,8 @@ web::json::value web::json::value::string(utility::string_t value, bool has_esca ); } -#ifdef _WIN32 -web::json::value web::json::value::string(const std::string& value) +#if defined(_UTF16_STRINGS) +web::json::value web::json::value::string(utility::nstring_view_t value) { std::unique_ptr ptr = utility::details::make_unique(utility::conversions::to_utf16string(value)); @@ -355,44 +355,44 @@ bool json::value::is_double() const return m_value->is_double(); } -json::value& web::json::details::_Object::index(const utility::string_t& key) { return m_object[key]; } +json::value& web::json::details::_Object::index(utility::string_view_t key) { return m_object[key]; } -bool web::json::details::_Object::has_field(const utility::string_t& key) const +bool web::json::details::_Object::has_field(utility::string_view_t key) const { return m_object.find(key) != m_object.end(); } -bool web::json::value::has_number_field(const utility::string_t& key) const +bool web::json::value::has_number_field(utility::string_view_t key) const { return has_field(key) && at(key).is_number(); } -bool web::json::value::has_integer_field(const utility::string_t& key) const +bool web::json::value::has_integer_field(utility::string_view_t key) const { return has_field(key) && at(key).is_integer(); } -bool web::json::value::has_double_field(const utility::string_t& key) const +bool web::json::value::has_double_field(utility::string_view_t key) const { return has_field(key) && at(key).is_double(); } -bool web::json::value::has_boolean_field(const utility::string_t& key) const +bool web::json::value::has_boolean_field(utility::string_view_t key) const { return has_field(key) && at(key).is_boolean(); } -bool web::json::value::has_string_field(const utility::string_t& key) const +bool web::json::value::has_string_field(utility::string_view_t key) const { return has_field(key) && at(key).is_string(); } -bool web::json::value::has_array_field(const utility::string_t& key) const +bool web::json::value::has_array_field(utility::string_view_t key) const { return has_field(key) && at(key).is_array(); } -bool web::json::value::has_object_field(const utility::string_t& key) const +bool web::json::value::has_object_field(utility::string_view_t key) const { return has_field(key) && at(key).is_object(); } @@ -428,18 +428,18 @@ bool json::value::operator==(const json::value& other) const void web::json::value::erase(size_t index) { return this->as_array().erase(index); } -void web::json::value::erase(const utility::string_t& key) { return this->as_object().erase(key); } +void web::json::value::erase(utility::string_view_t key) { return this->as_object().erase(key); } // at() overloads web::json::value& web::json::value::at(size_t index) { return this->as_array().at(index); } const web::json::value& web::json::value::at(size_t index) const { return this->as_array().at(index); } -web::json::value& web::json::value::at(const utility::string_t& key) { return this->as_object().at(key); } +web::json::value& web::json::value::at(utility::string_view_t key) { return this->as_object().at(key); } -const web::json::value& web::json::value::at(const utility::string_t& key) const { return this->as_object().at(key); } +const web::json::value& web::json::value::at(utility::string_view_t key) const { return this->as_object().at(key); } -web::json::value& web::json::value::operator[](const utility::string_t& key) +web::json::value& web::json::value::operator[](utility::string_view_t key) { if (this->is_null()) { diff --git a/Release/src/json/json_parsing.cpp b/Release/src/json/json_parsing.cpp index 30873fd36d..e5719d3494 100644 --- a/Release/src/json/json_parsing.cpp +++ b/Release/src/json/json_parsing.cpp @@ -46,7 +46,7 @@ __declspec(noreturn) #else __attribute__((noreturn)) #endif - void CreateException(const Token& tk, const utility::string_t& message) + void CreateException(const Token& tk, utility::string_view_t message) { std::string str("* Line "); str += std::to_string(tk.start.m_line); @@ -211,7 +211,7 @@ template class JSON_StringParser : public JSON_Parser { public: - JSON_StringParser(const std::basic_string& string) : m_position(&string[0]) + JSON_StringParser(utility::string_view string) : m_position(string.data()) { m_startpos = m_position; m_endpos = m_position + string.size(); @@ -356,24 +356,32 @@ inline bool JSON_Parser::ParseInt64(CharType first, uint64_t& value) // This namespace hides the x-plat helper functions namespace { -#if defined(_WIN32) +#ifdef _WIN32 + static int print_llu(char* ptr, size_t n, uint64_t val64) { return _snprintf_s_l(ptr, n, _TRUNCATE, "%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); } +#if defined(_UTF16_STRINGS) static int print_llu(wchar_t* ptr, size_t n, uint64_t val64) { return _snwprintf_s_l(ptr, n, _TRUNCATE, L"%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); } +#endif + static double anystod(const char* str) { return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); } + +#if defined(_UTF16_STRINGS) static double anystod(const wchar_t* str) { return _wcstod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); } +#endif + #else static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long long val64) { @@ -1166,7 +1174,7 @@ static web::json::value _parse_stream(utility::istream_t& stream, std::error_cod return returnObject; } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) static web::json::value _parse_narrow_stream(std::istream& stream) { web::json::details::JSON_StreamParser parser(stream); @@ -1215,7 +1223,7 @@ static web::json::value _parse_narrow_stream(std::istream& stream, std::error_co } #endif -web::json::value web::json::value::parse(const utility::string_t& str) +web::json::value web::json::value::parse(utility::string_view_t str) { web::json::details::JSON_StringParser parser(str); web::json::details::JSON_Parser::Token tkn; @@ -1239,7 +1247,7 @@ web::json::value web::json::value::parse(const utility::string_t& str) return value; } -web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error) +web::json::value web::json::value::parse(utility::string_view_t str, std::error_code& error) { web::json::details::JSON_StringParser parser(str); web::json::details::JSON_Parser::Token tkn; @@ -1269,7 +1277,7 @@ web::json::value web::json::value::parse(utility::istream_t& stream, std::error_ return _parse_stream(stream, error); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) web::json::value web::json::value::parse(std::istream& stream) { return _parse_narrow_stream(stream); } web::json::value web::json::value::parse(std::istream& stream, std::error_code& error) diff --git a/Release/src/json/json_serialization.cpp b/Release/src/json/json_serialization.cpp index 3aa0c50ceb..9652bc23db 100644 --- a/Release/src/json/json_serialization.cpp +++ b/Release/src/json/json_serialization.cpp @@ -29,7 +29,7 @@ using namespace utility::conversions; // JSON Serialization // -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) void web::json::value::serialize(std::ostream& stream) const { // This has better performance than writing directly to stream. @@ -56,7 +56,7 @@ void web::json::value::format(std::basic_string& string) const { m_value-> template void web::json::details::append_escape_string(std::basic_string& str, - const std::basic_string& escaped) + utility::string_view escaped) { for (const auto& ch : escaped) { @@ -112,18 +112,18 @@ void web::json::details::append_escape_string(std::basic_string& str, } } -void web::json::details::format_string(const utility::string_t& key, utility::string_t& str) +void web::json::details::format_string(utility::string_view_t key, utility::string_t& str) { str.push_back('"'); - append_escape_string(str, key); + append_escape_string(str, key); str.push_back('"'); } -#ifdef _WIN32 -void web::json::details::format_string(const utility::string_t& key, std::string& str) +#if defined(_UTF16_STRINGS) +void web::json::details::format_string(utility::string_view_t key, std::string& str) { str.push_back('"'); - append_escape_string(str, utility::conversions::to_utf8string(key)); + append_escape_string(str, utility::conversions::to_utf8string(key)); str.push_back('"'); } #endif @@ -134,7 +134,7 @@ void web::json::details::_String::format(std::basic_string& str) const if (m_has_escape_char) { - append_escape_string(str, utility::conversions::to_utf8string(m_string)); + append_escape_string(str, utility::conversions::to_utf8string(m_string)); } else { @@ -190,7 +190,7 @@ void web::json::details::_Number::format(std::basic_string& stream) const } } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) void web::json::details::_String::format(std::basic_string& str) const { @@ -198,7 +198,7 @@ void web::json::details::_String::format(std::basic_string& str) const if (m_has_escape_char) { - append_escape_string(str, m_string); + append_escape_string(str, m_string); } else { diff --git a/Release/src/pch/stdafx.h b/Release/src/pch/stdafx.h index 2061bea7f6..372bc561ff 100644 --- a/Release/src/pch/stdafx.h +++ b/Release/src/pch/stdafx.h @@ -92,7 +92,10 @@ // utilities #include "cpprest/asyncrt_utils.h" +#include "cpprest/base64_utils.h" #include "cpprest/details/web_utilities.h" +#include "cpprest/memory_utils.h" +#include "cpprest/string_utils.h" // http #include "cpprest/details/http_helpers.h" diff --git a/Release/src/streams/fileio_win32.cpp b/Release/src/streams/fileio_win32.cpp index 97cd6e5e6d..82657d61bd 100644 --- a/Release/src/streams/fileio_win32.cpp +++ b/Release/src/streams/fileio_win32.cpp @@ -228,7 +228,11 @@ bool __cdecl _open_fsb_str(_In_ _filestream_callback* callback, _ASSERTE(callback != nullptr); _ASSERTE(filename != nullptr); - std::wstring name(filename); + std::wstring name( +#if defined(CPPREST_FORCE_NARROW_STRINGS) + conversions::to_utf16string +#endif + (filename)); pplx::create_task([=]() { DWORD dwDesiredAccess, dwCreationDisposition, dwShareMode; diff --git a/Release/src/utilities/asyncrt_utils.cpp b/Release/src/utilities/asyncrt_utils.cpp index 5263c8d5d5..d16531ce7a 100644 --- a/Release/src/utilities/asyncrt_utils.cpp +++ b/Release/src/utilities/asyncrt_utils.cpp @@ -13,201 +13,17 @@ #include "stdafx.h" -#include #include + +#include #include #include #include -using namespace web; using namespace utility; -using namespace utility::conversions; - -namespace -{ -struct to_lower_ch_impl -{ - char operator()(char c) const CPPREST_NOEXCEPT - { - if (c >= 'A' && c <= 'Z') return static_cast(c - 'A' + 'a'); - return c; - } - - wchar_t operator()(wchar_t c) const CPPREST_NOEXCEPT - { - if (c >= L'A' && c <= L'Z') return static_cast(c - L'A' + L'a'); - return c; - } -}; - -CPPREST_CONSTEXPR to_lower_ch_impl to_lower_ch {}; - -struct eq_lower_ch_impl -{ - template - inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT - { - return to_lower_ch(left) == to_lower_ch(right); - } -}; - -CPPREST_CONSTEXPR eq_lower_ch_impl eq_lower_ch {}; - -struct lt_lower_ch_impl -{ - template - inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT - { - return to_lower_ch(left) < to_lower_ch(right); - } -}; - -CPPREST_CONSTEXPR lt_lower_ch_impl lt_lower_ch {}; -} // namespace namespace utility { -namespace details -{ -_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT -{ - return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch); -} - -_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT -{ - return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch); -} - -_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT -{ - return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch); -} - -_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT -{ - return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch); -} - -_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT -{ - for (auto& ch : target) - { - ch = to_lower_ch(ch); - } -} - -_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT -{ - for (auto& ch : target) - { - ch = to_lower_ch(ch); - } -} - -#if !defined(ANDROID) && !defined(__ANDROID__) -std::once_flag g_c_localeFlag; -std::unique_ptr g_c_locale( - nullptr, [](scoped_c_thread_locale::xplat_locale*) {}); -scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() -{ - std::call_once(g_c_localeFlag, [&]() { - scoped_c_thread_locale::xplat_locale* clocale = new scoped_c_thread_locale::xplat_locale(); -#ifdef _WIN32 - *clocale = _create_locale(LC_ALL, "C"); - if (clocale == nullptr || *clocale == nullptr) - { - throw std::runtime_error("Unable to create 'C' locale."); - } - auto deleter = [](scoped_c_thread_locale::xplat_locale* clocale) { - _free_locale(*clocale); - delete clocale; - }; -#else - *clocale = newlocale(LC_ALL, "C", nullptr); - if (clocale == nullptr || *clocale == nullptr) - { - throw std::runtime_error("Unable to create 'C' locale."); - } - auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) - { - freelocale(*clocale); - delete clocale; - }; -#endif - g_c_locale = - std::unique_ptr( - clocale, deleter); - }); - return *g_c_locale; -} -#endif - -#ifdef _WIN32 -scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1) -{ - char* prevLocale = setlocale(LC_ALL, nullptr); - if (prevLocale == nullptr) - { - throw std::runtime_error("Unable to retrieve current locale."); - } - - if (std::strcmp(prevLocale, "C") != 0) - { - m_prevLocale = prevLocale; - m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - if (m_prevThreadSetting == -1) - { - throw std::runtime_error("Unable to enable per thread locale."); - } - if (setlocale(LC_ALL, "C") == nullptr) - { - _configthreadlocale(m_prevThreadSetting); - throw std::runtime_error("Unable to set locale"); - } - } -} - -scoped_c_thread_locale::~scoped_c_thread_locale() -{ - if (m_prevThreadSetting != -1) - { - setlocale(LC_ALL, m_prevLocale.c_str()); - _configthreadlocale(m_prevThreadSetting); - } -} -#elif (defined(ANDROID) || defined(__ANDROID__)) -scoped_c_thread_locale::scoped_c_thread_locale() {} -scoped_c_thread_locale::~scoped_c_thread_locale() {} -#else -scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr) -{ - char* prevLocale = setlocale(LC_ALL, nullptr); - if (prevLocale == nullptr) - { - throw std::runtime_error("Unable to retrieve current locale."); - } - - if (std::strcmp(prevLocale, "C") != 0) - { - m_prevLocale = uselocale(c_locale()); - if (m_prevLocale == nullptr) - { - throw std::runtime_error("Unable to set locale"); - } - } -} - -scoped_c_thread_locale::~scoped_c_thread_locale() -{ - if (m_prevLocale != nullptr) - { - uselocale(m_prevLocale); - } -} -#endif -} // namespace details - namespace details { const std::error_category& __cdecl platform_category() @@ -302,321 +118,6 @@ const std::error_category& __cdecl linux_category() } // namespace details -#define LOW_3BITS 0x7 -#define LOW_4BITS 0xF -#define LOW_5BITS 0x1F -#define LOW_6BITS 0x3F -#define BIT4 0x8 -#define BIT5 0x10 -#define BIT6 0x20 -#define BIT7 0x40 -#define BIT8 0x80 -#define L_SURROGATE_START 0xDC00 -#define L_SURROGATE_END 0xDFFF -#define H_SURROGATE_START 0xD800 -#define H_SURROGATE_END 0xDBFF -#define SURROGATE_PAIR_START 0x10000 - -// Create a dedicated type for characters to avoid the issue -// of different platforms defaulting char to be either signed -// or unsigned. -using UtilCharInternal_t = signed char; - -inline size_t count_utf8_to_utf16(const std::string& s) -{ - const size_t sSize = s.size(); - auto const sData = reinterpret_cast(s.data()); - size_t result {sSize}; - - for (size_t index = 0; index < sSize;) - { - if (sData[index] >= 0) - { - // use fast inner loop to skip single byte code points (which are - // expected to be the most frequent) - while ((++index < sSize) && (sData[index] >= 0)) - ; - - if (index >= sSize) break; - } - - // start special handling for multi-byte code points - const UtilCharInternal_t c {sData[index++]}; - - if ((c & BIT7) == 0) - { - throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); - } - else if ((c & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF - { - if (index == sSize) - { - throw std::range_error("UTF-8 string is missing bytes in character"); - } - - const UtilCharInternal_t c2 {sData[index++]}; - if ((c2 & 0xC0) != BIT8) - { - throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); - } - - // can't require surrogates for 7FF - --result; - } - else if ((c & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF - { - if (sSize - index < 2) - { - throw std::range_error("UTF-8 string is missing bytes in character"); - } - - const UtilCharInternal_t c2 {sData[index++]}; - const UtilCharInternal_t c3 {sData[index++]}; - if (((c2 | c3) & 0xC0) != BIT8) - { - throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); - } - - result -= 2; - } - else if ((c & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF - { - if (sSize - index < 3) - { - throw std::range_error("UTF-8 string is missing bytes in character"); - } - - const UtilCharInternal_t c2 {sData[index++]}; - const UtilCharInternal_t c3 {sData[index++]}; - const UtilCharInternal_t c4 {sData[index++]}; - if (((c2 | c3 | c4) & 0xC0) != BIT8) - { - throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); - } - - const uint32_t codePoint = - ((c & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS); - result -= (3 - (codePoint >= SURROGATE_PAIR_START)); - } - else - { - throw std::range_error("UTF-8 string has invalid Unicode code point"); - } - } - - return result; -} - -utf16string __cdecl conversions::utf8_to_utf16(const std::string& s) -{ - // Save repeated heap allocations, use the length of resulting sequence. - const size_t srcSize = s.size(); - auto const srcData = reinterpret_cast(s.data()); - utf16string dest(count_utf8_to_utf16(s), L'\0'); - utf16string::value_type* const destData = &dest[0]; - size_t destIndex = 0; - - for (size_t index = 0; index < srcSize; ++index) - { - UtilCharInternal_t src = srcData[index]; - switch (src & 0xF0) - { - case 0xF0: // 4 byte character, 0x10000 to 0x10FFFF - { - const UtilCharInternal_t c2 {srcData[++index]}; - const UtilCharInternal_t c3 {srcData[++index]}; - const UtilCharInternal_t c4 {srcData[++index]}; - uint32_t codePoint = - ((src & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS); - if (codePoint >= SURROGATE_PAIR_START) - { - // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. - // - 0x10000 is subtracted from the code point - // - high surrogate is 0xD800 added to the top ten bits - // - low surrogate is 0xDC00 added to the low ten bits - codePoint -= SURROGATE_PAIR_START; - destData[destIndex++] = static_cast((codePoint >> 10) | H_SURROGATE_START); - destData[destIndex++] = - static_cast((codePoint & 0x3FF) | L_SURROGATE_START); - } - else - { - // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point - // value. U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present - // but will encode them if encountered. - destData[destIndex++] = static_cast(codePoint); - } - } - break; - case 0xE0: // 3 byte character, 0x800 to 0xFFFF - { - const UtilCharInternal_t c2 {srcData[++index]}; - const UtilCharInternal_t c3 {srcData[++index]}; - destData[destIndex++] = static_cast( - ((src & LOW_4BITS) << 12) | ((c2 & LOW_6BITS) << 6) | (c3 & LOW_6BITS)); - } - break; - case 0xD0: // 2 byte character, 0x80 to 0x7FF - case 0xC0: - { - const UtilCharInternal_t c2 {srcData[++index]}; - destData[destIndex++] = - static_cast(((src & LOW_5BITS) << 6) | (c2 & LOW_6BITS)); - } - break; - default: // single byte character, 0x0 to 0x7F - // try to use a fast inner loop for following single byte characters, - // since they are quite probable - do - { - destData[destIndex++] = static_cast(srcData[index++]); - } while (index < srcSize && srcData[index] > 0); - // adjust index since it will be incremented by the for loop - --index; - } - } - return dest; -} - -inline size_t count_utf16_to_utf8(const utf16string& w) -{ - const utf16string::value_type* const srcData = &w[0]; - const size_t srcSize = w.size(); - size_t destSize(srcSize); - for (size_t index = 0; index < srcSize; ++index) - { - const utf16string::value_type ch(srcData[index]); - if (ch <= 0x7FF) - { - if (ch > 0x7F) // 2 bytes needed (11 bits used) - { - ++destSize; - } - } - // Check for high surrogate. - else if (ch >= H_SURROGATE_START && ch <= H_SURROGATE_END) // 4 bytes needed (21 bits used) - { - ++index; - if (index == srcSize) - { - throw std::range_error("UTF-16 string is missing low surrogate"); - } - - const auto lowSurrogate = srcData[index]; - if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) - { - throw std::range_error("UTF-16 string has invalid low surrogate"); - } - - destSize += 2; - } - else // 3 bytes needed (16 bits used) - { - destSize += 2; - } - } - - return destSize; -} - -std::string __cdecl conversions::utf16_to_utf8(const utf16string& w) -{ - const size_t srcSize = w.size(); - const utf16string::value_type* const srcData = &w[0]; - std::string dest(count_utf16_to_utf8(w), '\0'); - std::string::value_type* const destData = &dest[0]; - size_t destIndex(0); - - for (size_t index = 0; index < srcSize; ++index) - { - const utf16string::value_type src = srcData[index]; - if (src <= 0x7FF) - { - if (src <= 0x7F) // single byte character - { - destData[destIndex++] = static_cast(src); - } - else // 2 bytes needed (11 bits used) - { - destData[destIndex++] = static_cast(char((src >> 6) | 0xC0)); // leading 5 bits - destData[destIndex++] = static_cast(char((src & LOW_6BITS) | BIT8)); // trailing 6 bits - } - } - // Check for high surrogate. - else if (src >= H_SURROGATE_START && src <= H_SURROGATE_END) - { - const auto highSurrogate = src; - const auto lowSurrogate = srcData[++index]; - - // To get from surrogate pair to Unicode code point: - // - subtract 0xD800 from high surrogate, this forms top ten bits - // - subtract 0xDC00 from low surrogate, this forms low ten bits - // - add 0x10000 - // Leaves a code point in U+10000 to U+10FFFF range. - uint32_t codePoint = highSurrogate - H_SURROGATE_START; - codePoint <<= 10; - codePoint |= lowSurrogate - L_SURROGATE_START; - codePoint += SURROGATE_PAIR_START; - - // 4 bytes needed (21 bits used) - destData[destIndex++] = static_cast((codePoint >> 18) | 0xF0); // leading 3 bits - destData[destIndex++] = static_cast(((codePoint >> 12) & LOW_6BITS) | BIT8); // next 6 bits - destData[destIndex++] = static_cast(((codePoint >> 6) & LOW_6BITS) | BIT8); // next 6 bits - destData[destIndex++] = static_cast((codePoint & LOW_6BITS) | BIT8); // trailing 6 bits - } - else // 3 bytes needed (16 bits used) - { - destData[destIndex++] = static_cast((src >> 12) | 0xE0); // leading 4 bits - destData[destIndex++] = static_cast(((src >> 6) & LOW_6BITS) | BIT8); // middle 6 bits - destData[destIndex++] = static_cast((src & LOW_6BITS) | BIT8); // trailing 6 bits - } - } - - return dest; -} - -utf16string __cdecl conversions::usascii_to_utf16(const std::string& s) -{ - // Ascii is a subset of UTF-8 so just convert to UTF-16 - return utf8_to_utf16(s); -} - -utf16string __cdecl conversions::latin1_to_utf16(const std::string& s) -{ - // Latin1 is the first 256 code points in Unicode. - // In UTF-16 encoding each of these is represented as exactly the numeric code point. - utf16string dest; - // Prefer resize combined with for-loop over constructor dest(s.begin(), s.end()) - // for faster assignment. - dest.resize(s.size()); - for (size_t i = 0; i < s.size(); ++i) - { - dest[i] = utf16char(static_cast(s[i])); - } - return dest; -} - -utf8string __cdecl conversions::latin1_to_utf8(const std::string& s) { return utf16_to_utf8(latin1_to_utf16(s)); } - -#ifndef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(utf16string&& s) { return utf16_to_utf8(std::move(s)); } -#endif - -#ifdef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(std::string&& s) { return utf8_to_utf16(std::move(s)); } -#endif - -#ifndef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(const utf16string& s) { return utf16_to_utf8(s); } -#endif - -#ifdef _UTF16_STRINGS -utility::string_t __cdecl conversions::to_string_t(const std::string& s) { return utf8_to_utf16(s); } -#endif - -std::string __cdecl conversions::to_utf8string(const utf16string& value) { return utf16_to_utf8(value); } - -utf16string __cdecl conversions::to_utf16string(const std::string& value) { return utf8_to_utf16(value); } static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds) diff --git a/Release/src/utilities/base64.cpp b/Release/src/utilities/base64.cpp index 1cb235c3b5..db419d5ec9 100644 --- a/Release/src/utilities/base64.cpp +++ b/Release/src/utilities/base64.cpp @@ -10,13 +10,16 @@ ****/ #include "stdafx.h" -using namespace web; +#include "cpprest/base64_utils.h" + +#include + using namespace utility; -std::vector _from_base64(const utility::string_t& str); +std::vector _from_base64(utility::string_view_t str); utility::string_t _to_base64(const unsigned char* ptr, size_t size); -std::vector __cdecl conversions::from_base64(const utility::string_t& str) { return _from_base64(str); } +std::vector __cdecl conversions::from_base64(utility::string_view_t str) { return _from_base64(str); } utility::string_t __cdecl conversions::to_base64(const std::vector& input) { @@ -26,7 +29,7 @@ utility::string_t __cdecl conversions::to_base64(const std::vector _from_base64(const utility::string_t& input) +std::vector _from_base64(utility::string_view_t input) { std::vector result; @@ -124,7 +127,7 @@ std::vector _from_base64(const utility::string_t& input) } auto size = input.size(); - const char_t* ptr = &input[0]; + const char_t* ptr = input.data(); auto outsz = (size / 4) * 3; outsz -= padding; diff --git a/Release/src/utilities/string_utils.cpp b/Release/src/utilities/string_utils.cpp new file mode 100644 index 0000000000..03f94f5e32 --- /dev/null +++ b/Release/src/utilities/string_utils.cpp @@ -0,0 +1,527 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Utilities + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ + +#include "stdafx.h" + +#include + +#include +#include +#include + +using namespace utility; +using namespace utility::conversions; + +namespace +{ +struct to_lower_ch_impl +{ + char operator()(char c) const CPPREST_NOEXCEPT + { + if (c >= 'A' && c <= 'Z') return static_cast(c - 'A' + 'a'); + return c; + } + + wchar_t operator()(wchar_t c) const CPPREST_NOEXCEPT + { + if (c >= L'A' && c <= L'Z') return static_cast(c - L'A' + L'a'); + return c; + } +}; + +CPPREST_CONSTEXPR to_lower_ch_impl to_lower_ch {}; + +struct eq_lower_ch_impl +{ + template + inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT + { + return to_lower_ch(left) == to_lower_ch(right); + } +}; + +CPPREST_CONSTEXPR eq_lower_ch_impl eq_lower_ch {}; + +struct lt_lower_ch_impl +{ + template + inline CharT operator()(const CharT left, const CharT right) const CPPREST_NOEXCEPT + { + return to_lower_ch(left) < to_lower_ch(right); + } +}; + +CPPREST_CONSTEXPR lt_lower_ch_impl lt_lower_ch {}; +} // namespace + +namespace utility +{ +namespace details +{ +_ASYNCRTIMP bool __cdecl str_iequal(utility::nstring_view_t left, utility::nstring_view_t right) CPPREST_NOEXCEPT +{ + return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch); +} + +_ASYNCRTIMP bool __cdecl str_iequal(utility::wstring_view_t left, utility::wstring_view_t right) CPPREST_NOEXCEPT +{ + return left.size() == right.size() && std::equal(left.cbegin(), left.cend(), right.cbegin(), eq_lower_ch); +} + +_ASYNCRTIMP bool __cdecl str_iless(utility::nstring_view_t left, utility::nstring_view_t right) CPPREST_NOEXCEPT +{ + return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch); +} + +_ASYNCRTIMP bool __cdecl str_iless(utility::wstring_view_t left, utility::wstring_view_t right) CPPREST_NOEXCEPT +{ + return std::lexicographical_compare(left.cbegin(), left.cend(), right.cbegin(), right.cend(), lt_lower_ch); +} + +_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT +{ + for (auto& ch : target) + { + ch = to_lower_ch(ch); + } +} + +_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT +{ + for (auto& ch : target) + { + ch = to_lower_ch(ch); + } +} + +#if !defined(ANDROID) && !defined(__ANDROID__) +std::once_flag g_c_localeFlag; +std::unique_ptr g_c_locale( + nullptr, [](scoped_c_thread_locale::xplat_locale*) {}); +scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() +{ + std::call_once(g_c_localeFlag, [&]() { + scoped_c_thread_locale::xplat_locale* clocale = new scoped_c_thread_locale::xplat_locale(); +#ifdef _WIN32 + *clocale = _create_locale(LC_ALL, "C"); + if (clocale == nullptr || *clocale == nullptr) + { + throw std::runtime_error("Unable to create 'C' locale."); + } + auto deleter = [](scoped_c_thread_locale::xplat_locale* clocale) { + _free_locale(*clocale); + delete clocale; + }; +#else + *clocale = newlocale(LC_ALL, "C", nullptr); + if (clocale == nullptr || *clocale == nullptr) + { + throw std::runtime_error("Unable to create 'C' locale."); + } + auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) + { + freelocale(*clocale); + delete clocale; + }; +#endif + g_c_locale = + std::unique_ptr( + clocale, deleter); + }); + return *g_c_locale; +} +#endif + +#ifdef _WIN32 +scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1) +{ + char* prevLocale = setlocale(LC_ALL, nullptr); + if (prevLocale == nullptr) + { + throw std::runtime_error("Unable to retrieve current locale."); + } + + if (std::strcmp(prevLocale, "C") != 0) + { + m_prevLocale = prevLocale; + m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + if (m_prevThreadSetting == -1) + { + throw std::runtime_error("Unable to enable per thread locale."); + } + if (setlocale(LC_ALL, "C") == nullptr) + { + _configthreadlocale(m_prevThreadSetting); + throw std::runtime_error("Unable to set locale"); + } + } +} + +scoped_c_thread_locale::~scoped_c_thread_locale() +{ + if (m_prevThreadSetting != -1) + { + setlocale(LC_ALL, m_prevLocale.c_str()); + _configthreadlocale(m_prevThreadSetting); + } +} +#elif (defined(ANDROID) || defined(__ANDROID__)) +scoped_c_thread_locale::scoped_c_thread_locale() {} +scoped_c_thread_locale::~scoped_c_thread_locale() {} +#else +scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr) +{ + char* prevLocale = setlocale(LC_ALL, nullptr); + if (prevLocale == nullptr) + { + throw std::runtime_error("Unable to retrieve current locale."); + } + + if (std::strcmp(prevLocale, "C") != 0) + { + m_prevLocale = uselocale(c_locale()); + if (m_prevLocale == nullptr) + { + throw std::runtime_error("Unable to set locale"); + } + } +} + +scoped_c_thread_locale::~scoped_c_thread_locale() +{ + if (m_prevLocale != nullptr) + { + uselocale(m_prevLocale); + } +} +#endif +} // namespace details + + +#define LOW_3BITS 0x7 +#define LOW_4BITS 0xF +#define LOW_5BITS 0x1F +#define LOW_6BITS 0x3F +#define BIT4 0x8 +#define BIT5 0x10 +#define BIT6 0x20 +#define BIT7 0x40 +#define BIT8 0x80 +#define L_SURROGATE_START 0xDC00 +#define L_SURROGATE_END 0xDFFF +#define H_SURROGATE_START 0xD800 +#define H_SURROGATE_END 0xDBFF +#define SURROGATE_PAIR_START 0x10000 + +// Create a dedicated type for characters to avoid the issue +// of different platforms defaulting char to be either signed +// or unsigned. +using UtilCharInternal_t = signed char; + +inline size_t count_utf8_to_utf16(utility::nstring_view_t s) +{ + const size_t sSize = s.size(); + auto const sData = reinterpret_cast(s.data()); + size_t result {sSize}; + + for (size_t index = 0; index < sSize;) + { + if (sData[index] >= 0) + { + // use fast inner loop to skip single byte code points (which are + // expected to be the most frequent) + while ((++index < sSize) && (sData[index] >= 0)) + ; + + if (index >= sSize) break; + } + + // start special handling for multi-byte code points + const UtilCharInternal_t c {sData[index++]}; + + if ((c & BIT7) == 0) + { + throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); + } + else if ((c & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF + { + if (index == sSize) + { + throw std::range_error("UTF-8 string is missing bytes in character"); + } + + const UtilCharInternal_t c2 {sData[index++]}; + if ((c2 & 0xC0) != BIT8) + { + throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); + } + + // can't require surrogates for 7FF + --result; + } + else if ((c & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF + { + if (sSize - index < 2) + { + throw std::range_error("UTF-8 string is missing bytes in character"); + } + + const UtilCharInternal_t c2 {sData[index++]}; + const UtilCharInternal_t c3 {sData[index++]}; + if (((c2 | c3) & 0xC0) != BIT8) + { + throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); + } + + result -= 2; + } + else if ((c & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF + { + if (sSize - index < 3) + { + throw std::range_error("UTF-8 string is missing bytes in character"); + } + + const UtilCharInternal_t c2 {sData[index++]}; + const UtilCharInternal_t c3 {sData[index++]}; + const UtilCharInternal_t c4 {sData[index++]}; + if (((c2 | c3 | c4) & 0xC0) != BIT8) + { + throw std::range_error("UTF-8 continuation byte is missing leading bit mask"); + } + + const uint32_t codePoint = + ((c & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS); + result -= (3 - (codePoint >= SURROGATE_PAIR_START)); + } + else + { + throw std::range_error("UTF-8 string has invalid Unicode code point"); + } + } + + return result; +} + +utf16string __cdecl conversions::utf8_to_utf16(utility::nstring_view_t s) +{ + // Save repeated heap allocations, use the length of resulting sequence. + const size_t srcSize = s.size(); + auto const srcData = reinterpret_cast(s.data()); + utf16string dest(count_utf8_to_utf16(s), L'\0'); + utf16string::value_type* const destData = &dest[0]; + size_t destIndex = 0; + + for (size_t index = 0; index < srcSize; ++index) + { + UtilCharInternal_t src = srcData[index]; + switch (src & 0xF0) + { + case 0xF0: // 4 byte character, 0x10000 to 0x10FFFF + { + const UtilCharInternal_t c2 {srcData[++index]}; + const UtilCharInternal_t c3 {srcData[++index]}; + const UtilCharInternal_t c4 {srcData[++index]}; + uint32_t codePoint = + ((src & LOW_3BITS) << 18) | ((c2 & LOW_6BITS) << 12) | ((c3 & LOW_6BITS) << 6) | (c4 & LOW_6BITS); + if (codePoint >= SURROGATE_PAIR_START) + { + // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. + // - 0x10000 is subtracted from the code point + // - high surrogate is 0xD800 added to the top ten bits + // - low surrogate is 0xDC00 added to the low ten bits + codePoint -= SURROGATE_PAIR_START; + destData[destIndex++] = static_cast((codePoint >> 10) | H_SURROGATE_START); + destData[destIndex++] = + static_cast((codePoint & 0x3FF) | L_SURROGATE_START); + } + else + { + // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point + // value. U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present + // but will encode them if encountered. + destData[destIndex++] = static_cast(codePoint); + } + } + break; + case 0xE0: // 3 byte character, 0x800 to 0xFFFF + { + const UtilCharInternal_t c2 {srcData[++index]}; + const UtilCharInternal_t c3 {srcData[++index]}; + destData[destIndex++] = static_cast( + ((src & LOW_4BITS) << 12) | ((c2 & LOW_6BITS) << 6) | (c3 & LOW_6BITS)); + } + break; + case 0xD0: // 2 byte character, 0x80 to 0x7FF + case 0xC0: + { + const UtilCharInternal_t c2 {srcData[++index]}; + destData[destIndex++] = + static_cast(((src & LOW_5BITS) << 6) | (c2 & LOW_6BITS)); + } + break; + default: // single byte character, 0x0 to 0x7F + // try to use a fast inner loop for following single byte characters, + // since they are quite probable + do + { + destData[destIndex++] = static_cast(srcData[index++]); + } while (index < srcSize && srcData[index] > 0); + // adjust index since it will be incremented by the for loop + --index; + } + } + return dest; +} + +inline size_t count_utf16_to_utf8(utf16string_view w) +{ + const utf16string::value_type* const srcData = w.data(); + const size_t srcSize = w.size(); + size_t destSize(srcSize); + for (size_t index = 0; index < srcSize; ++index) + { + const utf16string::value_type ch(srcData[index]); + if (ch <= 0x7FF) + { + if (ch > 0x7F) // 2 bytes needed (11 bits used) + { + ++destSize; + } + } + // Check for high surrogate. + else if (ch >= H_SURROGATE_START && ch <= H_SURROGATE_END) // 4 bytes needed (21 bits used) + { + ++index; + if (index == srcSize) + { + throw std::range_error("UTF-16 string is missing low surrogate"); + } + + const auto lowSurrogate = srcData[index]; + if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) + { + throw std::range_error("UTF-16 string has invalid low surrogate"); + } + + destSize += 2; + } + else // 3 bytes needed (16 bits used) + { + destSize += 2; + } + } + + return destSize; +} + +std::string __cdecl conversions::utf16_to_utf8(utf16string_view w) +{ + const size_t srcSize = w.size(); + const utf16string::value_type* const srcData = w.data(); + std::string dest(count_utf16_to_utf8(w), '\0'); + std::string::value_type* const destData = &dest[0]; + size_t destIndex(0); + + for (size_t index = 0; index < srcSize; ++index) + { + const utf16string::value_type src = srcData[index]; + if (src <= 0x7FF) + { + if (src <= 0x7F) // single byte character + { + destData[destIndex++] = static_cast(src); + } + else // 2 bytes needed (11 bits used) + { + destData[destIndex++] = static_cast(char((src >> 6) | 0xC0)); // leading 5 bits + destData[destIndex++] = static_cast(char((src & LOW_6BITS) | BIT8)); // trailing 6 bits + } + } + // Check for high surrogate. + else if (src >= H_SURROGATE_START && src <= H_SURROGATE_END) + { + const auto highSurrogate = src; + const auto lowSurrogate = srcData[++index]; + + // To get from surrogate pair to Unicode code point: + // - subtract 0xD800 from high surrogate, this forms top ten bits + // - subtract 0xDC00 from low surrogate, this forms low ten bits + // - add 0x10000 + // Leaves a code point in U+10000 to U+10FFFF range. + uint32_t codePoint = highSurrogate - H_SURROGATE_START; + codePoint <<= 10; + codePoint |= lowSurrogate - L_SURROGATE_START; + codePoint += SURROGATE_PAIR_START; + + // 4 bytes needed (21 bits used) + destData[destIndex++] = static_cast((codePoint >> 18) | 0xF0); // leading 3 bits + destData[destIndex++] = static_cast(((codePoint >> 12) & LOW_6BITS) | BIT8); // next 6 bits + destData[destIndex++] = static_cast(((codePoint >> 6) & LOW_6BITS) | BIT8); // next 6 bits + destData[destIndex++] = static_cast((codePoint & LOW_6BITS) | BIT8); // trailing 6 bits + } + else // 3 bytes needed (16 bits used) + { + destData[destIndex++] = static_cast((src >> 12) | 0xE0); // leading 4 bits + destData[destIndex++] = static_cast(((src >> 6) & LOW_6BITS) | BIT8); // middle 6 bits + destData[destIndex++] = static_cast((src & LOW_6BITS) | BIT8); // trailing 6 bits + } + } + + return dest; +} + +utf16string __cdecl conversions::usascii_to_utf16(utility::nstring_view_t s) +{ + // Ascii is a subset of UTF-8 so just convert to UTF-16 + return utf8_to_utf16(s); +} + +utf16string __cdecl conversions::latin1_to_utf16(utility::nstring_view_t s) +{ + // Latin1 is the first 256 code points in Unicode. + // In UTF-16 encoding each of these is represented as exactly the numeric code point. + utf16string dest; + // Prefer resize combined with for-loop over constructor dest(s.begin(), s.end()) + // for faster assignment. + dest.resize(s.size()); + for (size_t i = 0; i < s.size(); ++i) + { + dest[i] = utf16char(static_cast(s[i])); + } + return dest; +} + +utf8string __cdecl conversions::latin1_to_utf8(utility::nstring_view_t s) { return utf16_to_utf8(latin1_to_utf16(s)); } + +#ifndef _UTF16_STRINGS +utility::string_t __cdecl conversions::to_string_t(utf16string&& s) { return utf16_to_utf8(std::move(s)); } +#endif + +#ifdef _UTF16_STRINGS +utility::string_t __cdecl conversions::to_string_t(std::string&& s) { return utf8_to_utf16(std::move(s)); } +#endif + +#ifndef _UTF16_STRINGS +utility::string_t __cdecl conversions::to_string_t(utf16string_view s) { return utf16_to_utf8(s); } +#endif + +#ifdef _UTF16_STRINGS +utility::string_t __cdecl conversions::to_string_t(utility::nstring_view_t s) { return utf8_to_utf16(s); } +#endif + +std::string __cdecl conversions::to_utf8string(utf16string_view value) { return utf16_to_utf8(value); } + +utf16string __cdecl conversions::to_utf16string(utility::nstring_view_t value) { return utf8_to_utf16(value); } + +} // namespace utility diff --git a/Release/src/utilities/web_utilities.cpp b/Release/src/utilities/web_utilities.cpp index ce00078b79..2096396e47 100644 --- a/Release/src/utilities/web_utilities.cpp +++ b/Release/src/utilities/web_utilities.cpp @@ -47,15 +47,15 @@ void winrt_secure_zero_buffer(Windows::Storage::Streams::IBuffer ^ buffer) } } -winrt_encryption::winrt_encryption(const std::wstring& data) +winrt_encryption::winrt_encryption(utility::string_view_t data) { auto provider = ref new Windows::Security::Cryptography::DataProtection::DataProtectionProvider( ref new Platform::String(L"Local=user")); // Create buffer containing plain text password. Platform::ArrayReference arrayref( - reinterpret_cast(const_cast(data.c_str())), - static_cast(data.size()) * sizeof(std::wstring::value_type)); + reinterpret_cast(const_cast<::utility::string_t::value_type*>(data.data())), + static_cast(data.size()) * sizeof(::utility::string_t::value_type)); Windows::Storage::Streams::IBuffer ^ plaintext = Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(arrayref); m_buffer = pplx::create_task(provider->ProtectAsync(plaintext)); @@ -84,14 +84,14 @@ plaintext_string winrt_encryption::decrypt() const // Construct string and zero out memory from plain text buffer. auto data = plaintext_string( - new std::wstring(reinterpret_cast(rawPlaintext), plaintext->Length / 2)); + new ::utility::string_t(reinterpret_cast(rawPlaintext), plaintext->Length / 2)); SecureZeroMemory(rawPlaintext, plaintext->Length); return std::move(data); } #else // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv -win32_encryption::win32_encryption(const std::wstring& data) : m_numCharacters(data.size()) +win32_encryption::win32_encryption(::utility::string_view_t data) : m_numCharacters(data.size()) { // Early return because CryptProtectMemory crashes with empty string if (m_numCharacters == 0) @@ -99,12 +99,12 @@ win32_encryption::win32_encryption(const std::wstring& data) : m_numCharacters(d return; } - if (data.size() > (std::numeric_limits::max)() / sizeof(wchar_t)) + if (data.size() > (std::numeric_limits::max)() / sizeof(::utility::string_t::value_type)) { throw std::length_error("Encryption string too long"); } - const auto dataSizeDword = static_cast(data.size() * sizeof(wchar_t)); + const auto dataSizeDword = static_cast(data.size() * sizeof(::utility::string_t::value_type)); // Round up dataSizeDword to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE static_assert(CRYPTPROTECTMEMORY_BLOCK_SIZE == 16, "Power of 2 assumptions in this bit masking violated"); @@ -113,7 +113,7 @@ win32_encryption::win32_encryption(const std::wstring& data) : m_numCharacters(d assert((dataNumBytes % CRYPTPROTECTMEMORY_BLOCK_SIZE) == 0); assert(dataNumBytes >= dataSizeDword); m_buffer.resize(dataNumBytes); - memcpy_s(m_buffer.data(), m_buffer.size(), data.c_str(), dataNumBytes); + memcpy_s(m_buffer.data(), m_buffer.size(), data.data(), dataNumBytes); if (!CryptProtectMemory(m_buffer.data(), dataNumBytes, CRYPTPROTECTMEMORY_SAME_PROCESS)) { throw ::utility::details::create_system_error(GetLastError()); @@ -125,8 +125,8 @@ win32_encryption::~win32_encryption() { SecureZeroMemory(m_buffer.data(), m_buff plaintext_string win32_encryption::decrypt() const { // Copy the buffer and decrypt to avoid having to re-encrypt. - auto result = plaintext_string(new std::wstring(reinterpret_cast(m_buffer.data()), - m_buffer.size() / sizeof(wchar_t))); + auto result = plaintext_string(new ::utility::string_t(reinterpret_cast(m_buffer.data()), + m_buffer.size() / sizeof(::utility::string_t::value_type))); auto& data = *result; if (!m_buffer.empty()) { diff --git a/Release/tests/functional/json/fuzz_tests.cpp b/Release/tests/functional/json/fuzz_tests.cpp index a925bf9dfb..f9ee113aaa 100644 --- a/Release/tests/functional/json/fuzz_tests.cpp +++ b/Release/tests/functional/json/fuzz_tests.cpp @@ -41,7 +41,7 @@ SUITE(json_fuzz_tests) TEST(fuzz_json_parser, "Requires", "fuzzedinputfile") { - std::wstring ipfile = utility::conversions::to_utf16string(get_fuzzed_file_path()); + utility::string_t ipfile = utility::conversions::to_string_t(get_fuzzed_file_path()); if (true == ipfile.empty()) { VERIFY_IS_TRUE(false, "Input file is empty"); @@ -52,22 +52,27 @@ SUITE(json_fuzz_tests) concurrency::streams::container_buffer cbuf; fs.read_to_end(cbuf).get(); fs.close().get(); - auto json_str = cbuf.collection(); + auto utf8_json_str = cbuf.collection(); // Look for UTF-8 BOM - if ((uint8_t)json_str[0] != 0xEF || (uint8_t)json_str[1] != 0xBB || (uint8_t)json_str[2] != 0xBF) + if ((uint8_t)utf8_json_str[0] != 0xEF || (uint8_t)utf8_json_str[1] != 0xBB || (uint8_t)utf8_json_str[2] != 0xBF) { VERIFY_IS_TRUE(false, "Input file encoding is not UTF-8. Test will not parse the file."); return; } - auto utf16_json_str = utility::conversions::utf8_to_utf16(json_str); + auto json_str = utility::conversions::to_string_t(utf8_json_str); +#if defined(_UTF16_STRINGS) // UTF8 to UTF16 conversion will retain the BOM, remove it. - if (utf16_json_str.front() == 0xFEFF) utf16_json_str.erase(0, 1); + if (json_str.front() == 0xFEFF) json_str.erase(0, 1); +#else + // remove the BOM + json_str.erase(0, 3); +#endif try { - json::value::parse(std::move(utf16_json_str)); + json::value::parse(std::move(json_str)); std::cout << "Input file parsed successfully."; } catch (const json::json_exception& ex) diff --git a/Release/tests/functional/json/json_numbers_tests.cpp b/Release/tests/functional/json/json_numbers_tests.cpp index 538d44d367..79905a1de8 100644 --- a/Release/tests/functional/json/json_numbers_tests.cpp +++ b/Release/tests/functional/json/json_numbers_tests.cpp @@ -196,7 +196,7 @@ SUITE(json_numbers_tests) "Fails due to double conversion issues") { // JSON uses the C locale always and should therefore not be impacted by the process locale -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) std::string changedLocale("fr-FR"); #else std::string changedLocale("fr_FR.UTF-8"); diff --git a/Release/tests/functional/json/negative_parsing_tests.cpp b/Release/tests/functional/json/negative_parsing_tests.cpp index 33a678ab16..ef99cc5cb7 100644 --- a/Release/tests/functional/json/negative_parsing_tests.cpp +++ b/Release/tests/functional/json/negative_parsing_tests.cpp @@ -147,8 +147,7 @@ SUITE(negative_parsing_tests) verify_json_throws(stream); } -// Test using Windows only API. -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(wstream_left_over_chars) { std::wstringbuf buf; diff --git a/Release/tests/functional/json/parsing_tests.cpp b/Release/tests/functional/json/parsing_tests.cpp index cf5a261bf0..08d9be9315 100644 --- a/Release/tests/functional/json/parsing_tests.cpp +++ b/Release/tests/functional/json/parsing_tests.cpp @@ -649,7 +649,7 @@ SUITE(parsing_tests) TEST(non_default_locale, "Ignore:Android", "Locale unsupported on Android") { std::string originalLocale = setlocale(LC_ALL, nullptr); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) std::string changedLocale("fr-FR"); #else std::string changedLocale("fr_FR.utf8"); @@ -718,7 +718,7 @@ SUITE(parsing_tests) error_code_helper(arrayStringStream); error_code_helper(objStringStream); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) std::wstringbuf buf; buf.sputn(valueStr.c_str(), valueStr.size()); @@ -752,7 +752,7 @@ SUITE(parsing_tests) VERIFY_IS_TRUE(streamErr.value() > 0); VERIFY_IS_TRUE(parsedObject.is_null()); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) std::wstringbuf buf; buf.sputn(str.c_str(), str.size()); std::wistream iStream(&buf); diff --git a/Release/tests/functional/json/to_as_and_operators_tests.cpp b/Release/tests/functional/json/to_as_and_operators_tests.cpp index 6103adf6a4..797dbe15b0 100644 --- a/Release/tests/functional/json/to_as_and_operators_tests.cpp +++ b/Release/tests/functional/json/to_as_and_operators_tests.cpp @@ -499,7 +499,7 @@ SUITE(to_as_and_operators_tests) value.serialize(ss); VERIFY_ARE_EQUAL(len, ss.str().length()); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) // Check wide string implementation std::basic_stringstream wss; value.serialize(wss); diff --git a/Release/tests/functional/streams/fstreambuf_tests.cpp b/Release/tests/functional/streams/fstreambuf_tests.cpp index 190eb66b0c..ea8902d4ba 100644 --- a/Release/tests/functional/streams/fstreambuf_tests.cpp +++ b/Release/tests/functional/streams/fstreambuf_tests.cpp @@ -39,7 +39,7 @@ using namespace ::pplx; utility::string_t get_full_name(const utility::string_t& name); void fill_file(const utility::string_t& name, size_t repetitions = 1); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) void fill_file_w(const utility::string_t& name, size_t repetitions = 1); #endif @@ -179,7 +179,7 @@ SUITE(file_buffer_tests) VERIFY_IS_TRUE(close.is_done()); VERIFY_IS_FALSE(stream.is_open()); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(WriteSingleCharTest1w) { auto open = OPEN_W(U("WriteSingleCharTest1w.txt")); @@ -227,7 +227,7 @@ SUITE(file_buffer_tests) VERIFY_IS_TRUE(close.is_done()); VERIFY_IS_FALSE(stream.is_open()); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(WriteBufferTest1w) { auto open = OPEN_W(U("WriteBufferTest1w.txt")); @@ -345,7 +345,7 @@ SUITE(file_buffer_tests) t[i].wait(); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(ReadSingleChar_bumpcw) { utility::string_t fname = U("ReadSingleChar_bumpcw.txt"); @@ -467,7 +467,7 @@ SUITE(file_buffer_tests) VERIFY_IS_FALSE(stream.is_open()); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(ReadSingleChar_nextcw) { utility::string_t fname = U("ReadSingleChar_nextcw.txt"); @@ -558,7 +558,7 @@ SUITE(file_buffer_tests) VERIFY_IS_FALSE(stream.is_open()); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(ReadSingleChar_getc1w) { utility::string_t fname = U("ReadSingleChar_getc1w.txt"); @@ -669,7 +669,7 @@ SUITE(file_buffer_tests) VERIFY_IS_FALSE(stream.is_open()); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(ReadBuffer1w) { // Test that seeking works. @@ -881,7 +881,7 @@ SUITE(file_buffer_tests) VERIFY_ARE_EQUAL(istream.size(), 2600); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(file_size_w) { utility::string_t fname = U("file_size_w.txt"); @@ -912,9 +912,8 @@ SUITE(file_buffer_tests) TEST(read_one_byte_at_4G) { // Create a file with one byte. - string_t filename = U("read_one_byte_at_4G.txt"); // create a sparse file with sparse file apis - auto handle = CreateSparseFile(filename.c_str()); + auto handle = CreateSparseFile(TEXT("read_one_byte_at_4G.txt")); VERIFY_ARE_NOT_EQUAL(handle, INVALID_HANDLE_VALUE); // write 1 byte @@ -930,7 +929,7 @@ SUITE(file_buffer_tests) CloseHandle(handle); // read the file with casablanca streams - concurrency::streams::streambuf file_buf = OPEN(filename, std::ios_base::in).get(); + concurrency::streams::streambuf file_buf = OPEN(U("read_one_byte_at_4G.txt"), std::ios_base::in).get(); file_buf.seekoff(4 * 1024 * 1024 * 1024ll, ::std::ios_base::beg, ::std::ios_base::in); int aCharacter = file_buf.getc().get(); diff --git a/Release/tests/functional/streams/istream_tests.cpp b/Release/tests/functional/streams/istream_tests.cpp index 32cb545aa2..1067120967 100644 --- a/Release/tests/functional/streams/istream_tests.cpp +++ b/Release/tests/functional/streams/istream_tests.cpp @@ -69,7 +69,7 @@ void fill_file_with_lines(const utility::string_t& name, const std::string& end, stream << "abcdefghijklmnopqrstuvwxyz" << end; } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) // Disabling warning in test because we check for nullptr. #pragma warning(push) @@ -820,7 +820,7 @@ SUITE(istream_tests) VERIFY_ARE_EQUAL(str1, "abc"); VERIFY_ARE_EQUAL(str2, "defgsf"); } -#ifdef _WIN32 // On Linux, this becomes the exact copy of istream_extract_string1, hence disabled +#if defined(_UTF16_STRINGS) TEST(istream_extract_wstring_1) { producer_consumer_buffer rbuf; @@ -905,7 +905,7 @@ SUITE(istream_tests) VERIFY_ARE_EQUAL(i1, 1024); VERIFY_ARE_EQUAL(i2, (uint64_t)12000000000); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(istream_extract_int64w) { producer_consumer_buffer rbuf; @@ -972,7 +972,7 @@ SUITE(istream_tests) VERIFY_ARE_EQUAL(i2, (uint32_t)3000000000); VERIFY_THROWS(is.extract().get(), std::range_error); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(istream_extract_int32w) { producer_consumer_buffer rbuf; @@ -1042,7 +1042,7 @@ SUITE(istream_tests) VERIFY_THROWS(is.extract().get(), std::range_error); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(istream_extract_int16w) { producer_consumer_buffer rbuf; @@ -1110,7 +1110,7 @@ SUITE(istream_tests) VERIFY_ARE_EQUAL(i2, '1'); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(istream_extract_int8w) { producer_consumer_buffer rbuf; @@ -1181,7 +1181,7 @@ SUITE(istream_tests) VERIFY_THROWS(is.extract().get(), std::runtime_error); } -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) TEST(istream_extract_bool_w) { producer_consumer_buffer rbuf; @@ -1237,7 +1237,7 @@ SUITE(istream_tests) { istream_extract_long_impl( container_buffer("123 -567 120000000000000000000000000000000000000000000000")); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) istream_extract_long_impl(container_buffer(L"123 -567 12000000000")); #endif } @@ -1257,7 +1257,7 @@ SUITE(istream_tests) TEST(istream_extract_unsigned_long) { istream_extract_unsigned_long_impl(container_buffer("876 3 -44")); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) istream_extract_unsigned_long_impl(container_buffer(L"876 3 -44")); #endif } @@ -1265,7 +1265,7 @@ SUITE(istream_tests) TEST(istream_extract_long_long) { istream_extract_long_impl(container_buffer("123 -567 92233720368547758078")); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) istream_extract_long_impl(container_buffer(L"123 -567 92233720368547758078")); #endif } @@ -1273,7 +1273,7 @@ SUITE(istream_tests) TEST(istream_extract_unsigned_long_long) { istream_extract_unsigned_long_impl(container_buffer("876 3 -44")); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) istream_extract_unsigned_long_impl(container_buffer(L"876 3 -44")); #endif } @@ -1555,7 +1555,7 @@ SUITE(istream_tests) const std::string strValue = inStream.extract().get(); VERIFY_ARE_EQUAL("", strValue); -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) const std::wstring wstrValue = inStream.extract().get(); VERIFY_ARE_EQUAL(L"", wstrValue); #endif diff --git a/Release/tests/functional/utils/base64.cpp b/Release/tests/functional/utils/base64.cpp index 30ec199ef2..70225d24eb 100644 --- a/Release/tests/functional/utils/base64.cpp +++ b/Release/tests/functional/utils/base64.cpp @@ -13,6 +13,8 @@ #include "stdafx.h" +#include "cpprest/base64_utils.h" + using namespace utility; namespace tests diff --git a/Release/tests/functional/utils/nonce_generator_tests.cpp b/Release/tests/functional/utils/nonce_generator_tests.cpp index 92c4ea0f6c..99cf45fc93 100644 --- a/Release/tests/functional/utils/nonce_generator_tests.cpp +++ b/Release/tests/functional/utils/nonce_generator_tests.cpp @@ -13,6 +13,8 @@ #include "stdafx.h" +#include + using namespace utility; namespace tests diff --git a/Release/tests/functional/utils/strings.cpp b/Release/tests/functional/utils/strings.cpp index 261714333d..a754a8e4d8 100644 --- a/Release/tests/functional/utils/strings.cpp +++ b/Release/tests/functional/utils/strings.cpp @@ -333,7 +333,7 @@ SUITE(strings) std::locale changedLocale; try { -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) changedLocale = std::locale("fr-FR"); #else changedLocale = std::locale("fr_FR.UTF-8"); @@ -358,7 +358,7 @@ SUITE(strings) std::locale changedLocale; try { -#ifdef _WIN32 +#if defined(_UTF16_STRINGS) changedLocale = std::locale("fr-FR"); #else changedLocale = std::locale("fr_FR.UTF-8"); diff --git a/Release/tests/functional/utils/win32_encryption_tests.cpp b/Release/tests/functional/utils/win32_encryption_tests.cpp index a2be7cde5a..8b77d18988 100644 --- a/Release/tests/functional/utils/win32_encryption_tests.cpp +++ b/Release/tests/functional/utils/win32_encryption_tests.cpp @@ -26,7 +26,7 @@ SUITE(win32_encryption) { TEST(win32_encryption_random_string) { - utility::string_t rndStr = utility::conversions::to_string_t("random string"); + const auto rndStr = utility::conversions::to_string_t("random string"); web::details::win32_encryption enc(rndStr); VERIFY_ARE_EQUAL(*enc.decrypt(), rndStr); @@ -34,7 +34,7 @@ SUITE(win32_encryption) TEST(win32_encryption_empty_string) { - utility::string_t emptyStr = utility::conversions::to_string_t(""); + const auto emptyStr = utility::conversions::to_string_t(""); web::details::win32_encryption enc(emptyStr); VERIFY_ARE_EQUAL(*enc.decrypt(), emptyStr); diff --git a/azure-devops/build-windows.yml b/azure-devops/build-windows.yml index 45ea392a82..bbfa5bb465 100644 --- a/azure-devops/build-windows.yml +++ b/azure-devops/build-windows.yml @@ -2,6 +2,7 @@ parameters: name: 'Windows_VS2019_x86' targetPlatform: 'x86' image: 'windows-latest' + useStringViews: 'ON' jobs: - job: ${{ parameters.name }} @@ -30,7 +31,7 @@ jobs: cmakeBuildType: 'Debug' useVcpkgToolchainFile: true buildDirectory: $(Build.ArtifactStagingDirectory)/${{ parameters.targetPlatform }}_Debug - cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF' + cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF -DCPPREST_USE_STRING_VIEWS=${{ parameters.useStringViews }}' - script: | cd $(Build.ArtifactStagingDirectory)\${{ parameters.targetPlatform }}_Debug\Release\Binaries .\test_runner.exe *testd.dll @@ -43,7 +44,7 @@ jobs: cmakeBuildType: 'Release' useVcpkgToolchainFile: true buildDirectory: $(Build.ArtifactStagingDirectory)/${{ parameters.targetPlatform }}_Release - cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF' + cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF -DCPPREST_USE_STRING_VIEWS=${{ parameters.useStringViews }}' - script: | cd $(Build.ArtifactStagingDirectory)\${{ parameters.targetPlatform }}_Release\Release\Binaries .\test_runner.exe *test.dll diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 55f7683b3a..1eb56eaa8b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -26,11 +26,13 @@ jobs: name: 'Windows_VS2015_x86' targetPlatform: x86 image: 'vs2015-win2012r2' + useStringViews: 'OFF' - template: azure-devops/build-windows.yml parameters: name: 'Windows_VS2015_x64' targetPlatform: x64 image: 'vs2015-win2012r2' + useStringViews: 'OFF' - job: Windows_VS2019_UWP pool: vmImage: 'windows-latest' @@ -44,7 +46,7 @@ jobs: - task: CMake@1 inputs: workingDirectory: 'build.common' - cmakeArgs: '-A x64 -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 ..' + cmakeArgs: '-A x64 -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 -DCPPREST_USE_STRING_VIEWS=ON ..' - task: MSBuild@1 inputs: solution: 'build.common/ALL_BUILD.vcxproj'