1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2025-01-31 20:41:45 +01:00

New narrow() implementation

This commit is contained in:
Nekotekina 2016-08-14 20:22:07 +03:00
parent 0f87c4485d
commit b0f5796c90
2 changed files with 114 additions and 19 deletions

View File

@ -15,29 +15,35 @@
static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
{
const auto buf_size = source.size() + 1; // size + null terminator
// String size + null terminator
const std::size_t buf_size = source.size() + 1;
const int size = source.size() < INT_MAX ? static_cast<int>(buf_size) : (fmt::throw_exception("to_wchar(): invalid source length (0x%llx)", source.size()), 0);
// Safe size
const int size = narrow<int>(buf_size, "to_wchar" HERE);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size]); // allocate buffer assuming that length is the max possible size
// Buffer for max possible output length
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size]);
verify("to_wchar" HERE), MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get(), size);
return buffer;
}
static void to_utf8(std::string& result, const wchar_t* source)
static void to_utf8(std::string& out, const wchar_t* source)
{
const auto length = std::wcslen(source);
// String size
const std::size_t length = std::wcslen(source);
const int buf_size = length <= INT_MAX / 3 ? static_cast<int>(length) * 3 + 1 : (fmt::throw_exception("to_utf8(): invalid source length (0x%llx)", length), 0);
// Safe buffer size for max possible output length (including null terminator)
const int buf_size = narrow<int>(length * 3 + 1, "to_utf8" HERE);
result.resize(buf_size); // set max possible length for utf-8 + null terminator
// Resize buffer
out.resize(buf_size - 1);
const int nwritten = verify(WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &result.front(), buf_size, NULL, NULL), "to_utf8" HERE);
const int result = WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &out.front(), buf_size, NULL, NULL);
// fix the size, remove null terminator
result.resize(nwritten - 1);
// Fix the size
out.resize(verify(result, "to_utf8" HERE) - 1);
}
static time_t to_time(const ULARGE_INTEGER& ft)
@ -722,8 +728,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
u64 read(void* buffer, u64 count) override
{
// TODO (call ReadFile multiple times if count is too big)
const int size = ::narrow<int>(count, "Too big count" HERE);
EXPECTS(size >= 0);
const int size = narrow<int>(count, "file::read" HERE);
DWORD nread;
verify("file::read" HERE), ReadFile(m_handle, buffer, size, &nread, NULL);
@ -734,8 +739,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
u64 write(const void* buffer, u64 count) override
{
// TODO (call WriteFile multiple times if count is too big)
const int size = ::narrow<int>(count, "Too big count" HERE);
EXPECTS(size >= 0);
const int size = narrow<int>(count, "file::write" HERE);
DWORD nwritten;
verify("file::write" HERE), WriteFile(m_handle, buffer, size, &nwritten, NULL);

View File

@ -574,19 +574,110 @@ inline std::remove_reference_t<T>&& verify_move(T&& value, const char* cause, F&
return std::move(value);
}
// Narrow cast (throws on failure)
// narrow() function details
template <typename From, typename To = void, typename = void>
struct narrow_impl
{
// Temporarily (diagnostic)
static_assert(std::is_void<To>::value, "narrow_impl<> specialization not found");
// Returns true if value cannot be represented in type To
static constexpr bool test(const From& value)
{
// Unspecialized cases (including cast to void) always considered narrowing
return true;
}
};
// Unsigned to unsigned narrowing
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_unsigned<From>::value && std::is_unsigned<To>::value>>
{
static constexpr bool test(const From& value)
{
return sizeof(To) < sizeof(From) && static_cast<To>(value) != value;
}
};
// Signed to signed narrowing
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_signed<From>::value && std::is_signed<To>::value>>
{
static constexpr bool test(const From& value)
{
return sizeof(To) < sizeof(From) && static_cast<To>(value) != value;
}
};
// Unsigned to signed narrowing
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_unsigned<From>::value && std::is_signed<To>::value>>
{
static constexpr bool test(const From& value)
{
return sizeof(To) <= sizeof(From) && value > (static_cast<std::make_unsigned_t<To>>(-1) >> 1);
}
};
// Signed to unsigned narrowing (I)
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_signed<From>::value && std::is_unsigned<To>::value && sizeof(To) >= sizeof(From)>>
{
static constexpr bool test(const From& value)
{
return value < static_cast<From>(0);
}
};
// Signed to unsigned narrowing (II)
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_signed<From>::value && std::is_unsigned<To>::value && sizeof(To) < sizeof(From)>>
{
static constexpr bool test(const From& value)
{
return static_cast<std::make_unsigned_t<From>>(value) > static_cast<To>(-1);
}
};
// Enum to integer (TODO?)
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_enum<From>::value && std::is_integral<To>::value>>
: narrow_impl<std::underlying_type_t<From>, To>
{
};
// Integer to enum (TODO?)
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_integral<From>::value && std::is_enum<To>::value>>
: narrow_impl<From, std::underlying_type_t<To>>
{
};
// Enum to enum (TODO?)
template <typename From, typename To>
struct narrow_impl<From, To, std::enable_if_t<std::is_enum<From>::value && std::is_enum<To>::value>>
: narrow_impl<std::underlying_type_t<From>, std::underlying_type_t<To>>
{
};
// Simple type enabled (TODO?)
template <typename From, typename To>
struct narrow_impl<From, To, void_t<typename From::simple_type>>
: narrow_impl<simple_t<From>, To>
{
};
template <typename To = void, typename From, typename = decltype(static_cast<To>(std::declval<From>()))>
inline To narrow(const From& value, const char* msg = nullptr)
{
// Allow "narrowing to void" and ensure it always fails in this case
auto&& result = static_cast<std::conditional_t<std::is_void<To>::value, From, To>>(value);
if (std::is_void<To>::value || static_cast<From>(result) != value)
// Narrow check
if (narrow_impl<From, To>::test(value))
{
// Pack value as formatting argument
fmt::raw_narrow_error(msg, fmt::get_type_info<typename fmt_unveil<From>::type>(), fmt_unveil<From>::get(value));
}
return static_cast<std::conditional_t<std::is_void<To>::value, void, decltype(result)>>(result);
return static_cast<To>(value);
}
// Returns u32 size() for container