diff --git a/Release/include/cpprest/astreambuf.h b/Release/include/cpprest/astreambuf.h index 1dcb285ded..fab450b0eb 100644 --- a/Release/include/cpprest/astreambuf.h +++ b/Release/include/cpprest/astreambuf.h @@ -15,6 +15,7 @@ #include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" +#include "cpprest/details/char_traits.h" #include "pplx/pplxtasks.h" #include #include @@ -56,55 +57,28 @@ namespace streams /// /// The data type of the basic element of the stream. /// +namespace detail +{ template -struct char_traits : std::char_traits<_CharType> +struct char_traits : utility::CanUseStdCharTraits<_CharType>::TraitsType { /// /// Some synchronous functions will return this value if the operation /// requires an asynchronous call in a given situation. /// /// An int_type value which implies that an asynchronous call is required. - static typename std::char_traits<_CharType>::int_type requires_async() + static typename utility::CanUseStdCharTraits<_CharType>::TraitsType::int_type requires_async() { - return std::char_traits<_CharType>::eof() - 1; + return utility::CanUseStdCharTraits<_CharType>::TraitsType::eof() - 1; } }; +} +template struct char_traits : detail::char_traits<_CharType> { +}; #if !defined(_WIN32) -template<> -struct char_traits : private std::char_traits -{ -public: - typedef unsigned char char_type; - - using std::char_traits::eof; - using std::char_traits::int_type; - using std::char_traits::off_type; - using std::char_traits::pos_type; - - static size_t length(const unsigned char* str) - { - return std::char_traits::length(reinterpret_cast(str)); - } - - static void assign(unsigned char& left, const unsigned char& right) { left = right; } - static unsigned char* assign(unsigned char* left, size_t n, unsigned char value) - { - return reinterpret_cast( - std::char_traits::assign(reinterpret_cast(left), n, static_cast(value))); - } - - static unsigned char* copy(unsigned char* left, const unsigned char* right, size_t n) - { - return reinterpret_cast( - std::char_traits::copy(reinterpret_cast(left), reinterpret_cast(right), n)); - } - - static unsigned char* move(unsigned char* left, const unsigned char* right, size_t n) - { - return reinterpret_cast( - std::char_traits::move(reinterpret_cast(left), reinterpret_cast(right), n)); - } - +template <> struct char_traits : detail::char_traits { + typedef typename std::char_traits::int_type int_type; + static int_type eof() { return std::char_traits::eof(); } static int_type requires_async() { return eof() - 1; } }; #endif diff --git a/Release/include/cpprest/details/char_traits.h b/Release/include/cpprest/details/char_traits.h new file mode 100644 index 0000000000..6be58c6e84 --- /dev/null +++ b/Release/include/cpprest/details/char_traits.h @@ -0,0 +1,102 @@ +// +// Created by sigsegv on 6/28/25. +// + +#ifndef CPPRESTSDK_ROOT_CHAR_TRAITS_H +#define CPPRESTSDK_ROOT_CHAR_TRAITS_H + +#include +#include + +namespace utility { + +namespace detail { + +template struct IntTypeFor { + typedef typename std::conditional::value, unsigned long long int, long long int>::type type; +}; +template <> struct IntTypeFor { + typedef typename std::char_traits::int_type type; +}; +template <> struct IntTypeFor { + typedef typename std::make_unsigned::int_type>::type type; +}; + +template class DetailCharTraits +{ +public: + using char_type = T; + using int_type = typename IntTypeFor::type; + using off_type = std::streamoff; + using pos_type = std::streampos; + using state_type = mbstate_t; + + static void assign(char_type& r, const char_type& a) noexcept { r = a; } + static char_type to_char_type(int_type c) noexcept { return char_type(c); } + static int_type to_int_type(char_type c) noexcept { return c; } + static bool eq(char_type a, char_type b) noexcept { return a == b; } + static bool lt(char_type a, char_type b) noexcept { return a < b; } + static int compare(const char_type* s1,const char_type* s2,size_t n){ + for (; n--; ++s1, ++s2) { + if (!eq(*s1, *s2)) + return lt(*s1,*s2)?-1:1; + } + return 0; + } + static size_t length(const char_type* s){ + const char_type* p = s; + while (*p) + ++p; + return size_t(p - s); + } + static const char_type* find(const char_type* s,size_t n,const char_type& a){ + for (; n--; ++s) + { + if (eq(*s, a)) + return s; + return nullptr; + } + } + static char_type* move (char_type* r,const char_type* s,size_t n){ + return (char_type*)memmove(r, s, n * sizeof(char_type)); + } + static char_type* copy (char_type* r,const char_type* s,size_t n){ + return (char_type*)memcpy (r, s, n * sizeof(char_type)); + } + static char_type* assign(char_type* r,size_t n,char_type a){ + if (sizeof(char_type) == 1) + { + return (char_type*)memset(r, a, n); + } + else + { + for (char_type *s = r; n--; ++s) + { + *s = a; + } + } + } + static int_type eof() noexcept { return ~0u; } + static int_type not_eof(int_type c) noexcept { return c == eof() ? 0 : c; } +}; + +template struct CanUseStdCharTraits : public std::false_type +{ +public: + typedef DetailCharTraits TraitsType; +}; + +template struct CanUseStdCharTraits::eq(std::declval(), std::declval()))> : public std::true_type +{ +public: + typedef std::char_traits TraitsType; +}; + +} + +template struct CanUseStdCharTraits : detail::CanUseStdCharTraits::type>::type> { +}; + +} + +#endif // CPPRESTSDK_ROOT_CHAR_TRAITS_H diff --git a/Release/include/cpprest/streams.h b/Release/include/cpprest/streams.h index b6c3864028..73cfcc480a 100644 --- a/Release/include/cpprest/streams.h +++ b/Release/include/cpprest/streams.h @@ -16,6 +16,7 @@ #define CASA_STREAMS_H #include "cpprest/astreambuf.h" +#include "cpprest/details/char_traits.h" #include #include @@ -60,30 +61,95 @@ class basic_istream_helper concurrency::streams::streambuf m_buffer; }; -template +template::TraitsType> struct Value2StringFormatter { + struct SanitizeInput + { + const std::basic_string &operator () (const std::basic_string &input) + { + return input; + } + template std::basic_string operator () (const std::basic_string &input) + { + return {reinterpret_cast(input.c_str()), input.size()}; + } + const char *operator () (const char *input) { + return input; + } + const char *operator () (const unsigned char *input) + { + return reinterpret_cast(input); + } + template T operator () (T input) + { + return input; + } + }; + struct GenerateFormatOutput + { + std::basic_string &&operator() (std::basic_string &&result) + { + return std::move(result); + } + std::basic_string operator() (const std::basic_string &intermediate) + { + return {reinterpret_cast(intermediate.c_str()), intermediate.size()}; + } + }; template - static std::basic_string format(const T& val) + static std::basic_string format(const T& val) + { + typename std::conditional< + sizeof(CharType) == 1, + std::basic_ostringstream, + std::basic_ostringstream::type> + >::type ss; + SanitizeInput sanitizer; + ss << sanitizer(val); + typename std::conditional< + sizeof(CharType) == 1, + std::basic_string, + std::basic_string::type> + >::type str = ss.str(); + GenerateFormatOutput generateFormatOutput; + return generateFormatOutput(std::move(str)); + } +}; + +template +struct Value2StringFormatterUint8Format +{ + std::basic_string operator () (const T& val) { - std::basic_ostringstream ss; + std::basic_ostringstream ss; ss << val; - return ss.str(); + return reinterpret_cast(ss.str().c_str()); + } +}; + +template +struct Value2StringFormatterUint8Format> +{ + std::basic_string operator () ( + const std::basic_string::TraitsType>& val) + { + Value2StringFormatterUint8Format> format; + return format(reinterpret_cast&>(val)); } }; template<> struct Value2StringFormatter { - template - static std::basic_string format(const T& val) + template ::TraitsType> + static std::basic_string format(const T& val) { - std::basic_ostringstream ss; - ss << val; - return reinterpret_cast(ss.str().c_str()); + Value2StringFormatterUint8Format format; + return format(val); } - static std::basic_string format(const utf16string& val) + static std::basic_string::TraitsType> format(const utf16string& val) { return format(utility::conversions::utf16_to_utf8(val)); } @@ -262,7 +328,7 @@ class basic_ostream /// Write the specified string to the output stream. /// /// Input string. - pplx::task print(const std::basic_string& str) const + pplx::task print(const std::basic_string& str) const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; @@ -273,7 +339,7 @@ class basic_ostream } else { - auto sharedStr = std::make_shared>(str); + auto sharedStr = std::make_shared>(str); return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) { return size; }); @@ -294,7 +360,7 @@ class basic_ostream if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; // TODO in the future this could be improved to have Value2StringFormatter avoid another unnecessary copy // by putting the string on the heap before calling the print string overload. - return print(details::Value2StringFormatter::format(val)); + return print(details::Value2StringFormatter::format(val)); } /// diff --git a/Release/tests/functional/streams/memstream_tests.cpp b/Release/tests/functional/streams/memstream_tests.cpp index 3bdbd6812a..b47c50c9b4 100644 --- a/Release/tests/functional/streams/memstream_tests.cpp +++ b/Release/tests/functional/streams/memstream_tests.cpp @@ -8,6 +8,7 @@ * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ +#include "cpprest/details/char_traits.h" #include "stdafx.h" #if defined(__cplusplus_winrt) #include @@ -32,7 +33,7 @@ void streambuf_putc(StreamBufferType& wbuf) { VERIFY_IS_TRUE(wbuf.can_write()); - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((typename StreamBufferType::char_type)0); s.push_back((typename StreamBufferType::char_type)1); s.push_back((typename StreamBufferType::char_type)2); @@ -137,7 +138,7 @@ void streambuf_putn(StreamBufferType& wbuf) { VERIFY_IS_TRUE(wbuf.can_write()); - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((typename StreamBufferType::char_type)0); s.push_back((typename StreamBufferType::char_type)1); s.push_back((typename StreamBufferType::char_type)2); @@ -169,7 +170,7 @@ void streambuf_putn(concurrency::streams::rawptr_buffer& wbuf) typedef concurrency::streams::rawptr_buffer StreamBufferType; - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((CharType)0); s.push_back((CharType)1); s.push_back((CharType)2); @@ -198,7 +199,7 @@ void streambuf_putn(concurrency::streams::container_buffer& wbuf typedef concurrency::streams::container_buffer StreamBufferType; typedef typename concurrency::streams::container_buffer::char_type CharType; - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((CharType)0); s.push_back((CharType)1); s.push_back((CharType)2); @@ -553,7 +554,7 @@ void streambuf_putn_getn(StreamBufferType& rwbuf) VERIFY_IS_TRUE(rwbuf.can_read()); VERIFY_IS_TRUE(rwbuf.can_write()); VERIFY_IS_FALSE(rwbuf.is_eof()); - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((typename StreamBufferType::char_type)0); s.push_back((typename StreamBufferType::char_type)1); s.push_back((typename StreamBufferType::char_type)2); @@ -684,7 +685,7 @@ void streambuf_close_read_with_pending_read(StreamBufferType& rwbuf) VERIFY_IS_TRUE(rwbuf.can_write()); // Write 4 characters - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((typename StreamBufferType::char_type)0); s.push_back((typename StreamBufferType::char_type)1); s.push_back((typename StreamBufferType::char_type)2); @@ -726,7 +727,7 @@ void streambuf_close_write_with_pending_read(StreamBufferType& rwbuf) VERIFY_IS_TRUE(rwbuf.can_write()); // Write 4 characters - std::basic_string s; + std::basic_string::TraitsType> s; s.push_back((typename StreamBufferType::char_type)0); s.push_back((typename StreamBufferType::char_type)1); s.push_back((typename StreamBufferType::char_type)2); diff --git a/Release/tests/functional/streams/stdstream_tests.cpp b/Release/tests/functional/streams/stdstream_tests.cpp index 34b9b3af12..c294b02d0e 100644 --- a/Release/tests/functional/streams/stdstream_tests.cpp +++ b/Release/tests/functional/streams/stdstream_tests.cpp @@ -13,6 +13,7 @@ #include "cpprest/filestream.h" #include "cpprest/producerconsumerstream.h" #include "cpprest/rawptrstream.h" +#include "cpprest/details/char_traits.h" #if (!defined(_WIN32) || !defined(CPPREST_EXCLUDE_WEBSOCKETS)) && !defined(__cplusplus_winrt) #include @@ -303,7 +304,8 @@ SUITE(stdstreambuf_tests) const std::streamsize iterations = 100; - const std::string the_alphabet("abcdefghijklmnopqrstuvwxyz"); + const char *the_alphabet_characters = "abcdefghijklmnopqrstuvwxyz"; + const std::basic_string::TraitsType> the_alphabet(reinterpret_cast(the_alphabet_characters)); auto writer = pplx::create_task([ostream, iterations, the_alphabet]() { auto os = ostream; @@ -341,7 +343,8 @@ SUITE(stdstreambuf_tests) const std::streamsize iterations = 100; - const std::string the_alphabet("abcdefghijklmnopqrstuvwxyz"); + const char *the_alphabet_chars = "abcdefghijklmnopqrstuvwxyz"; + const std::basic_string::TraitsType> the_alphabet(reinterpret_cast(the_alphabet_chars)); auto writer = pplx::create_task([ostream, iterations, the_alphabet]() { auto os = ostream;