mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-04 14:01:25 +12:00
New narrow() implementation
This commit is contained in:
parent
0f87c4485d
commit
b0f5796c90
2 changed files with 114 additions and 19 deletions
|
@ -15,29 +15,35 @@
|
||||||
|
|
||||||
static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
|
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);
|
verify("to_wchar" HERE), MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get(), size);
|
||||||
|
|
||||||
return buffer;
|
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
|
// Fix the size
|
||||||
result.resize(nwritten - 1);
|
out.resize(verify(result, "to_utf8" HERE) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static time_t to_time(const ULARGE_INTEGER& ft)
|
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
|
u64 read(void* buffer, u64 count) override
|
||||||
{
|
{
|
||||||
// TODO (call ReadFile multiple times if count is too big)
|
// TODO (call ReadFile multiple times if count is too big)
|
||||||
const int size = ::narrow<int>(count, "Too big count" HERE);
|
const int size = narrow<int>(count, "file::read" HERE);
|
||||||
EXPECTS(size >= 0);
|
|
||||||
|
|
||||||
DWORD nread;
|
DWORD nread;
|
||||||
verify("file::read" HERE), ReadFile(m_handle, buffer, size, &nread, NULL);
|
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
|
u64 write(const void* buffer, u64 count) override
|
||||||
{
|
{
|
||||||
// TODO (call WriteFile multiple times if count is too big)
|
// TODO (call WriteFile multiple times if count is too big)
|
||||||
const int size = ::narrow<int>(count, "Too big count" HERE);
|
const int size = narrow<int>(count, "file::write" HERE);
|
||||||
EXPECTS(size >= 0);
|
|
||||||
|
|
||||||
DWORD nwritten;
|
DWORD nwritten;
|
||||||
verify("file::write" HERE), WriteFile(m_handle, buffer, size, &nwritten, NULL);
|
verify("file::write" HERE), WriteFile(m_handle, buffer, size, &nwritten, NULL);
|
||||||
|
|
|
@ -574,19 +574,110 @@ inline std::remove_reference_t<T>&& verify_move(T&& value, const char* cause, F&
|
||||||
return std::move(value);
|
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>()))>
|
template <typename To = void, typename From, typename = decltype(static_cast<To>(std::declval<From>()))>
|
||||||
inline To narrow(const From& value, const char* msg = nullptr)
|
inline To narrow(const From& value, const char* msg = nullptr)
|
||||||
{
|
{
|
||||||
// Allow "narrowing to void" and ensure it always fails in this case
|
// Narrow check
|
||||||
auto&& result = static_cast<std::conditional_t<std::is_void<To>::value, From, To>>(value);
|
if (narrow_impl<From, To>::test(value))
|
||||||
if (std::is_void<To>::value || static_cast<From>(result) != value)
|
|
||||||
{
|
{
|
||||||
// Pack value as formatting argument
|
// Pack value as formatting argument
|
||||||
fmt::raw_narrow_error(msg, fmt::get_type_info<typename fmt_unveil<From>::type>(), fmt_unveil<From>::get(value));
|
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
|
// Returns u32 size() for container
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue