|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
* Catch v2.2.1 |
|
|
|
|
|
* Generated: 2018-03-11 12:01:31.654719 |
|
|
|
|
|
|
|
|
* Catch v2.2.2 |
|
|
|
|
|
* Generated: 2018-04-06 12:05:03.186665 |
|
|
* ---------------------------------------------------------- |
|
|
* ---------------------------------------------------------- |
|
|
* This file has been merged from multiple headers. Please don't edit it directly |
|
|
* This file has been merged from multiple headers. Please don't edit it directly |
|
|
* Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. |
|
|
* Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CATCH_VERSION_MAJOR 2 |
|
|
#define CATCH_VERSION_MAJOR 2 |
|
|
#define CATCH_VERSION_MINOR 2 |
|
|
#define CATCH_VERSION_MINOR 2 |
|
|
#define CATCH_VERSION_PATCH 1 |
|
|
|
|
|
|
|
|
#define CATCH_VERSION_PATCH 2 |
|
|
|
|
|
|
|
|
#ifdef __clang__ |
|
|
#ifdef __clang__ |
|
|
# pragma clang system_header |
|
|
# pragma clang system_header |
|
|
|
|
|
|
|
|
# pragma clang diagnostic ignored "-Wcovered-switch-default" |
|
|
# pragma clang diagnostic ignored "-Wcovered-switch-default" |
|
|
# endif |
|
|
# endif |
|
|
#elif defined __GNUC__ |
|
|
#elif defined __GNUC__ |
|
|
# pragma GCC diagnostic ignored "-Wunused-variable" |
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wparentheses" |
|
|
# pragma GCC diagnostic ignored "-Wparentheses" |
|
|
# pragma GCC diagnostic push |
|
|
# pragma GCC diagnostic push |
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wunused-variable" |
|
|
# pragma GCC diagnostic ignored "-Wpadded" |
|
|
# pragma GCC diagnostic ignored "-Wpadded" |
|
|
#endif |
|
|
#endif |
|
|
// end catch_suppress_warnings.h |
|
|
// end catch_suppress_warnings.h |
|
|
|
|
|
|
|
|
return convertUnknownEnumToString( value ); |
|
|
return convertUnknownEnumToString( value ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#if defined(_MANAGED) |
|
|
|
|
|
//! Convert a CLR string to a utf8 std::string |
|
|
|
|
|
template<typename T> |
|
|
|
|
|
std::string clrReferenceToString( T^ ref ) { |
|
|
|
|
|
if (ref == nullptr) |
|
|
|
|
|
return std::string("null"); |
|
|
|
|
|
auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); |
|
|
|
|
|
cli::pin_ptr<System::Byte> p = &bytes[0]; |
|
|
|
|
|
return std::string(reinterpret_cast<char const *>(p), bytes->Length); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
} // namespace Detail |
|
|
} // namespace Detail |
|
|
|
|
|
|
|
|
// If we decide for C++14, change these to enable_if_ts |
|
|
// If we decide for C++14, change these to enable_if_ts |
|
|
|
|
|
|
|
|
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); |
|
|
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#if defined(_MANAGED) |
|
|
|
|
|
template <typename T> |
|
|
|
|
|
std::string stringify( T^ e ) { |
|
|
|
|
|
return ::Catch::StringMaker<T^>::convert(e); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
} // namespace Detail |
|
|
} // namespace Detail |
|
|
|
|
|
|
|
|
// Some predefined specializations |
|
|
// Some predefined specializations |
|
|
|
|
|
|
|
|
struct StringMaker<char *> { |
|
|
struct StringMaker<char *> { |
|
|
static std::string convert(char * str); |
|
|
static std::string convert(char * str); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
#ifdef CATCH_CONFIG_WCHAR |
|
|
#ifdef CATCH_CONFIG_WCHAR |
|
|
template<> |
|
|
template<> |
|
|
struct StringMaker<wchar_t const *> { |
|
|
struct StringMaker<wchar_t const *> { |
|
|
|
|
|
|
|
|
}; |
|
|
}; |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
// TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, |
|
|
|
|
|
// while keeping string semantics? |
|
|
template<int SZ> |
|
|
template<int SZ> |
|
|
struct StringMaker<char[SZ]> { |
|
|
struct StringMaker<char[SZ]> { |
|
|
static std::string convert(const char* str) { |
|
|
|
|
|
|
|
|
static std::string convert(char const* str) { |
|
|
return ::Catch::Detail::stringify(std::string{ str }); |
|
|
return ::Catch::Detail::stringify(std::string{ str }); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
template<int SZ> |
|
|
template<int SZ> |
|
|
struct StringMaker<signed char[SZ]> { |
|
|
struct StringMaker<signed char[SZ]> { |
|
|
static std::string convert(const char* str) { |
|
|
|
|
|
return ::Catch::Detail::stringify(std::string{ str }); |
|
|
|
|
|
|
|
|
static std::string convert(signed char const* str) { |
|
|
|
|
|
return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
template<int SZ> |
|
|
template<int SZ> |
|
|
struct StringMaker<unsigned char[SZ]> { |
|
|
struct StringMaker<unsigned char[SZ]> { |
|
|
static std::string convert(const char* str) { |
|
|
|
|
|
return ::Catch::Detail::stringify(std::string{ str }); |
|
|
|
|
|
|
|
|
static std::string convert(unsigned char const* str) { |
|
|
|
|
|
return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#if defined(_MANAGED) |
|
|
|
|
|
template <typename T> |
|
|
|
|
|
struct StringMaker<T^> { |
|
|
|
|
|
static std::string convert( T^ ref ) { |
|
|
|
|
|
return ::Catch::Detail::clrReferenceToString(ref); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
namespace Detail { |
|
|
namespace Detail { |
|
|
template<typename InputIterator> |
|
|
template<typename InputIterator> |
|
|
std::string rangeToString(InputIterator first, InputIterator last) { |
|
|
std::string rangeToString(InputIterator first, InputIterator last) { |
|
|
|
|
|
|
|
|
!std::is_same<decltype(end(std::declval<T>())), not_this_one>::value; |
|
|
!std::is_same<decltype(end(std::declval<T>())), not_this_one>::value; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#if defined(_MANAGED) // Managed types are never ranges |
|
|
|
|
|
template <typename T> |
|
|
|
|
|
struct is_range<T^> { |
|
|
|
|
|
static const bool value = false; |
|
|
|
|
|
}; |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
template<typename Range> |
|
|
template<typename Range> |
|
|
std::string rangeToString( Range const& range ) { |
|
|
std::string rangeToString( Range const& range ) { |
|
|
return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); |
|
|
return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); |
|
|
|
|
|
|
|
|
} // namespace Catch |
|
|
} // namespace Catch |
|
|
|
|
|
|
|
|
// end catch_matchers_floating.h |
|
|
// end catch_matchers_floating.h |
|
|
|
|
|
// start catch_matchers_generic.hpp |
|
|
|
|
|
|
|
|
|
|
|
#include <functional> |
|
|
|
|
|
#include <string> |
|
|
|
|
|
|
|
|
|
|
|
namespace Catch { |
|
|
|
|
|
namespace Matchers { |
|
|
|
|
|
namespace Generic { |
|
|
|
|
|
|
|
|
|
|
|
namespace Detail { |
|
|
|
|
|
std::string finalizeDescription(const std::string& desc); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
class PredicateMatcher : public MatcherBase<T> { |
|
|
|
|
|
std::function<bool(T const&)> m_predicate; |
|
|
|
|
|
std::string m_description; |
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr) |
|
|
|
|
|
:m_predicate(std::move(elem)), |
|
|
|
|
|
m_description(Detail::finalizeDescription(descr)) |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
|
|
|
bool match( T const& item ) const override { |
|
|
|
|
|
return m_predicate(item); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::string describe() const override { |
|
|
|
|
|
return m_description; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
} // namespace Generic |
|
|
|
|
|
|
|
|
|
|
|
// The following functions create the actual matcher objects. |
|
|
|
|
|
// The user has to explicitly specify type to the function, because |
|
|
|
|
|
// infering std::function<bool(T const&)> is hard (but possible) and |
|
|
|
|
|
// requires a lot of TMP. |
|
|
|
|
|
template<typename T> |
|
|
|
|
|
Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") { |
|
|
|
|
|
return Generic::PredicateMatcher<T>(predicate, description); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // namespace Matchers |
|
|
|
|
|
} // namespace Catch |
|
|
|
|
|
|
|
|
|
|
|
// end catch_matchers_generic.hpp |
|
|
// start catch_matchers_string.h |
|
|
// start catch_matchers_string.h |
|
|
|
|
|
|
|
|
#include <string> |
|
|
#include <string> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
public: |
|
|
// !TBD We need to do this another way! |
|
|
// !TBD We need to do this another way! |
|
|
bool aborting() const override; |
|
|
|
|
|
|
|
|
bool aborting() const final; |
|
|
|
|
|
|
|
|
private: |
|
|
private: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Performs equivalent check of std::fabs(lhs - rhs) <= margin |
|
|
// Performs equivalent check of std::fabs(lhs - rhs) <= margin |
|
|
// But without the subtraction to allow for INFINITY in comparison |
|
|
// But without the subtraction to allow for INFINITY in comparison |
|
|
bool WithinAbsMatcher::match(double const& matchee) const { |
|
|
bool WithinAbsMatcher::match(double const& matchee) const { |
|
|
return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin); |
|
|
|
|
|
|
|
|
return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::string WithinAbsMatcher::describe() const { |
|
|
std::string WithinAbsMatcher::describe() const { |
|
|
|
|
|
|
|
|
} // namespace Catch |
|
|
} // namespace Catch |
|
|
|
|
|
|
|
|
// end catch_matchers_floating.cpp |
|
|
// end catch_matchers_floating.cpp |
|
|
|
|
|
// start catch_matchers_generic.cpp |
|
|
|
|
|
|
|
|
|
|
|
std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { |
|
|
|
|
|
if (desc.empty()) { |
|
|
|
|
|
return "matches undescribed predicate"; |
|
|
|
|
|
} else { |
|
|
|
|
|
return "matches predicate: \"" + desc + '"'; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// end catch_matchers_generic.cpp |
|
|
// start catch_matchers_string.cpp |
|
|
// start catch_matchers_string.cpp |
|
|
|
|
|
|
|
|
#include <regex> |
|
|
#include <regex> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <chrono> |
|
|
#include <chrono> |
|
|
|
|
|
|
|
|
|
|
|
static const uint64_t nanosecondsInSecond = 1000000000; |
|
|
|
|
|
|
|
|
namespace Catch { |
|
|
namespace Catch { |
|
|
|
|
|
|
|
|
auto getCurrentNanosecondsSinceEpoch() -> uint64_t { |
|
|
auto getCurrentNanosecondsSinceEpoch() -> uint64_t { |
|
|
|
|
|
|
|
|
uint64_t sum = 0; |
|
|
uint64_t sum = 0; |
|
|
static const uint64_t iterations = 1000000; |
|
|
static const uint64_t iterations = 1000000; |
|
|
|
|
|
|
|
|
|
|
|
auto startTime = getCurrentNanosecondsSinceEpoch(); |
|
|
|
|
|
|
|
|
for( std::size_t i = 0; i < iterations; ++i ) { |
|
|
for( std::size_t i = 0; i < iterations; ++i ) { |
|
|
|
|
|
|
|
|
uint64_t ticks; |
|
|
uint64_t ticks; |
|
|
uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); |
|
|
uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); |
|
|
do { |
|
|
do { |
|
|
ticks = getCurrentNanosecondsSinceEpoch(); |
|
|
ticks = getCurrentNanosecondsSinceEpoch(); |
|
|
} |
|
|
|
|
|
while( ticks == baseTicks ); |
|
|
|
|
|
|
|
|
} while( ticks == baseTicks ); |
|
|
|
|
|
|
|
|
auto delta = ticks - baseTicks; |
|
|
auto delta = ticks - baseTicks; |
|
|
sum += delta; |
|
|
sum += delta; |
|
|
|
|
|
|
|
|
|
|
|
// If we have been calibrating for over 3 seconds -- the clock |
|
|
|
|
|
// is terrible and we should move on. |
|
|
|
|
|
// TBD: How to signal that the measured resolution is probably wrong? |
|
|
|
|
|
if (ticks > startTime + 3 * nanosecondsInSecond) { |
|
|
|
|
|
return sum / i; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// We're just taking the mean, here. To do better we could take the std. dev and exclude outliers |
|
|
// We're just taking the mean, here. To do better we could take the std. dev and exclude outliers |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Version const& libraryVersion() { |
|
|
Version const& libraryVersion() { |
|
|
static Version version( 2, 2, 1, "", 0 ); |
|
|
|
|
|
|
|
|
static Version version( 2, 2, 2, "", 0 ); |
|
|
return version; |
|
|
return version; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <iomanip> |
|
|
#include <iomanip> |
|
|
|
|
|
|
|
|
|
|
|
using uchar = unsigned char; |
|
|
|
|
|
|
|
|
namespace Catch { |
|
|
namespace Catch { |
|
|
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
|
|
size_t trailingBytes(unsigned char c) { |
|
|
|
|
|
if ((c & 0xE0) == 0xC0) { |
|
|
|
|
|
return 2; |
|
|
|
|
|
} |
|
|
|
|
|
if ((c & 0xF0) == 0xE0) { |
|
|
|
|
|
return 3; |
|
|
|
|
|
} |
|
|
|
|
|
if ((c & 0xF8) == 0xF0) { |
|
|
|
|
|
return 4; |
|
|
|
|
|
} |
|
|
|
|
|
CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint32_t headerValue(unsigned char c) { |
|
|
|
|
|
if ((c & 0xE0) == 0xC0) { |
|
|
|
|
|
return c & 0x1F; |
|
|
|
|
|
} |
|
|
|
|
|
if ((c & 0xF0) == 0xE0) { |
|
|
|
|
|
return c & 0x0F; |
|
|
|
|
|
} |
|
|
|
|
|
if ((c & 0xF8) == 0xF0) { |
|
|
|
|
|
return c & 0x07; |
|
|
|
|
|
} |
|
|
|
|
|
CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void hexEscapeChar(std::ostream& os, unsigned char c) { |
|
|
|
|
|
os << "\\x" |
|
|
|
|
|
<< std::uppercase << std::hex << std::setfill('0') << std::setw(2) |
|
|
|
|
|
<< static_cast<int>(c); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace |
|
|
|
|
|
|
|
|
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) |
|
|
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) |
|
|
: m_str( str ), |
|
|
: m_str( str ), |
|
|
m_forWhat( forWhat ) |
|
|
m_forWhat( forWhat ) |
|
|
{} |
|
|
{} |
|
|
|
|
|
|
|
|
void XmlEncode::encodeTo( std::ostream& os ) const { |
|
|
void XmlEncode::encodeTo( std::ostream& os ) const { |
|
|
|
|
|
|
|
|
// Apostrophe escaping not necessary if we always use " to write attributes |
|
|
// Apostrophe escaping not necessary if we always use " to write attributes |
|
|
// (see: http://www.w3.org/TR/xml/#syntax) |
|
|
// (see: http://www.w3.org/TR/xml/#syntax) |
|
|
|
|
|
|
|
|
for( std::size_t i = 0; i < m_str.size(); ++ i ) { |
|
|
|
|
|
char c = m_str[i]; |
|
|
|
|
|
switch( c ) { |
|
|
|
|
|
case '<': os << "<"; break; |
|
|
|
|
|
case '&': os << "&"; break; |
|
|
|
|
|
|
|
|
for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { |
|
|
|
|
|
uchar c = m_str[idx]; |
|
|
|
|
|
switch (c) { |
|
|
|
|
|
case '<': os << "<"; break; |
|
|
|
|
|
case '&': os << "&"; break; |
|
|
|
|
|
|
|
|
case '>': |
|
|
|
|
|
// See: http://www.w3.org/TR/xml/#syntax |
|
|
|
|
|
if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) |
|
|
|
|
|
os << ">"; |
|
|
|
|
|
else |
|
|
|
|
|
os << c; |
|
|
|
|
|
|
|
|
case '>': |
|
|
|
|
|
// See: http://www.w3.org/TR/xml/#syntax |
|
|
|
|
|
if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') |
|
|
|
|
|
os << ">"; |
|
|
|
|
|
else |
|
|
|
|
|
os << c; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
case '\"': |
|
|
|
|
|
if (m_forWhat == ForAttributes) |
|
|
|
|
|
os << """; |
|
|
|
|
|
else |
|
|
|
|
|
os << c; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
// Check for control characters and invalid utf-8 |
|
|
|
|
|
|
|
|
|
|
|
// Escape control characters in standard ascii |
|
|
|
|
|
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 |
|
|
|
|
|
if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { |
|
|
|
|
|
hexEscapeChar(os, c); |
|
|
break; |
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
case '\"': |
|
|
|
|
|
if( m_forWhat == ForAttributes ) |
|
|
|
|
|
os << """; |
|
|
|
|
|
else |
|
|
|
|
|
os << c; |
|
|
|
|
|
|
|
|
// Plain ASCII: Write it to stream |
|
|
|
|
|
if (c < 0x7F) { |
|
|
|
|
|
os << c; |
|
|
break; |
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
// Escape control chars - based on contribution by @espenalb in PR #465 and |
|
|
|
|
|
// by @mrpi PR #588 |
|
|
|
|
|
if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { |
|
|
|
|
|
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 |
|
|
|
|
|
os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) |
|
|
|
|
|
<< static_cast<int>( c ); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
os << c; |
|
|
|
|
|
|
|
|
// UTF-8 territory |
|
|
|
|
|
// Check if the encoding is valid and if it is not, hex escape bytes. |
|
|
|
|
|
// Important: We do not check the exact decoded values for validity, only the encoding format |
|
|
|
|
|
// First check that this bytes is a valid lead byte: |
|
|
|
|
|
// This means that it is not encoded as 1111 1XXX |
|
|
|
|
|
// Or as 10XX XXXX |
|
|
|
|
|
if (c < 0xC0 || |
|
|
|
|
|
c >= 0xF8) { |
|
|
|
|
|
hexEscapeChar(os, c); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto encBytes = trailingBytes(c); |
|
|
|
|
|
// Are there enough bytes left to avoid accessing out-of-bounds memory? |
|
|
|
|
|
if (idx + encBytes - 1 >= m_str.size()) { |
|
|
|
|
|
hexEscapeChar(os, c); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
// The header is valid, check data |
|
|
|
|
|
// The next encBytes bytes must together be a valid utf-8 |
|
|
|
|
|
// This means: bitpattern 10XX XXXX and the extracted value is sane (ish) |
|
|
|
|
|
bool valid = true; |
|
|
|
|
|
uint32_t value = headerValue(c); |
|
|
|
|
|
for (std::size_t n = 1; n < encBytes; ++n) { |
|
|
|
|
|
uchar nc = m_str[idx + n]; |
|
|
|
|
|
valid &= ((nc & 0xC0) == 0x80); |
|
|
|
|
|
value = (value << 6) | (nc & 0x3F); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( |
|
|
|
|
|
// Wrong bit pattern of following bytes |
|
|
|
|
|
(!valid) || |
|
|
|
|
|
// Overlong encodings |
|
|
|
|
|
(value < 0x80) || |
|
|
|
|
|
(0x80 <= value && value < 0x800 && encBytes > 2) || |
|
|
|
|
|
(0x800 < value && value < 0x10000 && encBytes > 3) || |
|
|
|
|
|
// Encoded value out of range |
|
|
|
|
|
(value >= 0x110000) |
|
|
|
|
|
) { |
|
|
|
|
|
hexEscapeChar(os, c); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If we got here, this is in fact a valid(ish) utf-8 sequence |
|
|
|
|
|
for (std::size_t n = 0; n < encBytes; ++n) { |
|
|
|
|
|
os << m_str[idx + n]; |
|
|
|
|
|
} |
|
|
|
|
|
idx += encBytes - 1; |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |