summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorRye <rye@alchemyviewer.org>2025-10-14 07:20:19 -0400
committerRye <rye@alchemyviewer.org>2025-10-27 04:59:13 -0400
commit2e162e985cc1ba3c1ca60bdbef3db5382bc105fb (patch)
tree44d014f812a3b33fb08398a76e86b23f403f1152 /indra/llcommon
parent579d8fbf82b622b6e071cf8fbc5770238644eb85 (diff)
Cherrypick 8de8daca601dc85e2b73687856f0a321016b4463 from archive/develop
Signed-off-by: Rye <rye@alchemyviewer.org>
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/lldefs.h55
1 files changed, 52 insertions, 3 deletions
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
index 232987da14..0481e45351 100644
--- a/indra/llcommon/lldefs.h
+++ b/indra/llcommon/lldefs.h
@@ -28,6 +28,7 @@
#define LL_LLDEFS_H
#include "stdtypes.h"
+#include <cassert>
#include <type_traits>
// Often used array indices
@@ -169,6 +170,38 @@ constexpr U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 +
// llclampb(a) // clamps a to [0 .. 255]
//
+// llless(d0, d1) safely compares d0 < d1 even if one is signed and the other
+// is unsigned. A simple (d0 < d1) expression converts the signed operand to
+// unsigned before comparing. If the signed operand is negative, that flips
+// the negative value to a huge positive value, producing the wrong answer!
+// llless() specifically addresses that case.
+template <typename T0, typename T1>
+constexpr bool llless(T0 d0, T1 d1)
+{
+ if constexpr (std::is_signed_v<T0> && ! std::is_signed_v<T1>)
+ {
+ // T0 signed, T1 unsigned: negative d0 is less than any unsigned d1
+ if (d0 < 0)
+ return true;
+ // both are non-negative: explicitly cast to avoid C4018
+ return std::make_unsigned_t<T0>(d0) < d1;
+ }
+ else if constexpr (! std::is_signed_v<T0> && std::is_signed_v<T1>)
+ {
+ // T0 unsigned, T1 signed: any unsigned d0 is greater than negative d1
+ if (d1 < 0)
+ return false;
+ // both are non-negative: explicitly cast to avoid C4018
+ return d0 < std::make_unsigned_t<T1>(d1);
+ }
+ else
+ {
+ // both T0 and T1 are signed, or both are unsigned:
+ // straightforward comparison works
+ return d0 < d1;
+ }
+}
+
// recursion tail
template <typename T>
constexpr auto llmax(T data)
@@ -180,7 +213,7 @@ template <typename T0, typename T1, typename... Ts>
constexpr auto llmax(T0 d0, T1 d1, Ts... rest)
{
auto maxrest = llmax(d1, rest...);
- return (d0 > maxrest)? d0 : maxrest;
+ return llless(maxrest, d0)? d0 : maxrest;
}
// recursion tail
@@ -194,12 +227,28 @@ template <typename T0, typename T1, typename... Ts>
constexpr auto llmin(T0 d0, T1 d1, Ts... rest)
{
auto minrest = llmin(d1, rest...);
- return (d0 < minrest) ? d0 : minrest;
+ return llless(d0, minrest) ? d0 : minrest;
}
template <typename A, typename MIN, typename MAX>
constexpr A llclamp(A a, MIN minval, MAX maxval)
{
+ // The only troublesome case is if A is unsigned and either minval or
+ // maxval is both signed and negative. Casting a negative number to
+ // unsigned flips it to a huge positive number, making this llclamp() call
+ // ineffective.
+ if constexpr (! std::is_signed_v<A>)
+ {
+ if constexpr (std::is_signed_v<MIN>)
+ {
+ assert(minval >= 0);
+ }
+ if constexpr (std::is_signed_v<MAX>)
+ {
+ assert(maxval >= 0);
+ }
+ }
+
A aminval{ static_cast<A>(minval) }, amaxval{ static_cast<A>(maxval) };
if ( a < aminval )
{
@@ -225,7 +274,7 @@ constexpr LLDATATYPE llclampb(LLDATATYPE a)
}
template <class LLDATATYPE>
-inline void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
+constexpr void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
{
std::swap(lhs, rhs);
}