Skip to content
Snippets Groups Projects
Commit cd997f21 authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

DRTVWR-575: Introduce llssize (signed size_t) and narrow() function.

llssize is for a function parameter that should accept a size or index
(derived from size_t, which is 64 bits in a 64-bit viewer) but might need to
go negative for flag values. We've historically used S32 for that purpose, but
Xcode 14.1 complains about trying to pass size_t to S32.

narrow() is a template function that casts a wider type (e.g. size_t or
llssize) to a narrower type (e.g. S32 or U32), with validation in
RelWithDebInfo builds. It verifies (using assert()) that the value being
truncated can in fact fit into the target type.
parent a2a723f3
No related branches found
No related tags found
No related merge requests found
...@@ -26,16 +26,23 @@ ...@@ -26,16 +26,23 @@
#ifndef LL_STDTYPES_H #ifndef LL_STDTYPES_H
#define LL_STDTYPES_H #define LL_STDTYPES_H
#include <cassert>
#include <cfloat> #include <cfloat>
#include <climits> #include <climits>
#include <limits>
#include <type_traits>
typedef signed char S8; typedef signed char S8;
typedef unsigned char U8; typedef unsigned char U8;
typedef signed short S16; typedef signed short S16;
typedef unsigned short U16; typedef unsigned short U16;
typedef signed int S32; typedef signed int S32;
typedef unsigned int U32; typedef unsigned int U32;
// to express an index that might go negative
// (ssize_t is provided by SOME compilers, don't collide)
typedef typename std::make_signed<size_t>::type llssize;
#if LL_WINDOWS #if LL_WINDOWS
// https://docs.microsoft.com/en-us/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type // https://docs.microsoft.com/en-us/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type
// https://docs.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp // https://docs.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp
...@@ -45,7 +52,7 @@ typedef unsigned int U32; ...@@ -45,7 +52,7 @@ typedef unsigned int U32;
// The version of clang available with VS 2019 also defines wchar_t as __wchar_t // The version of clang available with VS 2019 also defines wchar_t as __wchar_t
// which is also 16 bits. // which is also 16 bits.
// In any case, llwchar should be a UTF-32 type. // In any case, llwchar should be a UTF-32 type.
typedef U32 llwchar; typedef U32 llwchar;
#else #else
typedef wchar_t llwchar; typedef wchar_t llwchar;
// What we'd actually want is a simple module-scope 'if constexpr' to test // What we'd actually want is a simple module-scope 'if constexpr' to test
...@@ -76,7 +83,7 @@ typedef double F64; ...@@ -76,7 +83,7 @@ typedef double F64;
typedef S32 BOOL; typedef S32 BOOL;
typedef U8 KEY; typedef U8 KEY;
typedef U32 MASK; typedef U32 MASK;
typedef U32 TPACKETID; typedef U32 TPACKETID;
// Use #define instead of consts to avoid conversion headaches // Use #define instead of consts to avoid conversion headaches
#define S8_MAX (SCHAR_MAX) #define S8_MAX (SCHAR_MAX)
...@@ -118,4 +125,95 @@ typedef U8 LLPCode; ...@@ -118,4 +125,95 @@ typedef U8 LLPCode;
typedef int intptr_t; typedef int intptr_t;
#endif #endif
/*****************************************************************************
* Narrowing
*****************************************************************************/
/**
* narrow() is used to cast a wider type to a narrower type with validation.
*
* In many cases we take the size() of a container and try to pass it to an
* S32 or a U32 parameter. We used to be able to assume that the size of
* anything we could fit into memory could be expressed as a 32-bit int. With
* 64-bit viewers, though, size_t as returned by size() and length() and so
* forth is 64 bits, and the compiler is unhappy about stuffing such values
* into 32-bit types.
*
* It works to force the compiler to truncate, e.g. static_cast<S32>(len) or
* S32(len) or (S32)len, but we can do better.
*
* For:
* @code
* std::vector<Object> container;
* void somefunc(S32 size);
* @endcode
* call:
* @code
* somefunc(narrow(container.size()));
* @endcode
*
* narrow() truncates but, in RelWithDebInfo builds, it validates (using
* assert()) that the passed value can validly be expressed by the destination
* type.
*/
// narrow_holder is a struct that accepts the passed value as its original
// type and provides templated conversion functions to other types. Once we're
// building with compilers that support Class Template Argument Deduction, we
// can rename this class template 'narrow' and eliminate the narrow() factory
// function below.
template <typename FROM>
class narrow_holder
{
private:
FROM mValue;
public:
narrow_holder(FROM value): mValue(value) {}
/*---------------------- Narrowing unsigned to signed ----------------------*/
template <typename TO,
typename std::enable_if<std::is_unsigned<FROM>::value &&
std::is_signed<TO>::value,
bool>::type = true>
inline
operator TO() const
{
// The reason we skip the
// assert(value >= std::numeric_limits<TO>::lowest());
// in the overload below is that to perform the above comparison, the
// compiler promotes the signed lowest() to the unsigned FROM type,
// making it hugely positive -- so a reasonable 'value' will always
// fail the assert().
assert(mValue <= std::numeric_limits<TO>::max());
return static_cast<TO>(mValue);
}
/*----------------------- Narrowing all other cases ------------------------*/
template <typename TO,
typename std::enable_if<! (std::is_unsigned<FROM>::value &&
std::is_signed<TO>::value),
bool>::type = true>
inline
operator TO() const
{
// two different assert()s so we can tell which condition failed
assert(mValue <= std::numeric_limits<TO>::max());
// Funny, with floating point types min() is "positive epsilon" rather
// than "largest negative" -- that's lowest().
assert(mValue >= std::numeric_limits<TO>::lowest());
// Do we really expect to use this with floating point types?
// If so, does it matter if a very small value truncates to zero?
//assert(fabs(mValue) >= std::numeric_limits<TO>::min());
return static_cast<TO>(mValue);
}
};
/// narrow() factory function returns a narrow_holder<FROM>(), which can be
/// implicitly converted to the target type.
template <typename FROM>
inline
narrow_holder<FROM> narrow(FROM value)
{
return { value };
}
#endif #endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment