Skip to content

Commit afb406d

Browse files
[CDRIVER-6043] Modify definitions in intutil to avoid compound-literals (#2056)
* Modify definitions in intutil to avoid compound-lits The followin changes are made: - We avoid compound literals expanded from macros. This is to avoid buggy compound-literal support in VS 2017 (See CDRIVER-6043). Instead, we pass the relevant argument to a function that initializes the new aggregate object. This has the downside that we cannot use the macros in constant expressions, but that was a minor feature that wasn't really used anywhere. - Add a new macro to define bit masks. This simplifies the expressions that build bit patterns used for two's complement arithmetic. - Rename the members of `mlib_upsized_integer` to give them meaningful names.
1 parent e388c29 commit afb406d

File tree

7 files changed

+110
-64
lines changed

7 files changed

+110
-64
lines changed

src/common/src/mlib/ckdint.h

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,17 @@ mlib_extern_c_begin ();
190190
// clang-format off
191191
// Generates an 0b11111 bit pattern for appropriate size:
192192
#define _mlibMaxofUnsigned(V) \
193-
(sizeof(V) == sizeof(uintmax_t) \
194-
? UINTMAX_MAX /* No funny bit math, just return the max of the max int */ \
195-
/* Generate an 0b11111... bit pattern */ \
196-
: (UINTMAX_C(1) << ( \
197-
/* Guard against an over-shift if V is uintmax_t: */ \
198-
(sizeof(V) < sizeof(uintmax_t)) \
199-
* (sizeof(V) * CHAR_BIT)) \
200-
) - 1)
193+
/* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \
194+
mlib_bits(mlib_bitsizeof((V)), 0)
201195

202196
// Generates an 0b01111 bit pattern for the two's complement max value:
203-
#define _mlibMaxofSigned(V) (_mlibMaxofUnsigned (V) >> 1ull)
197+
#define _mlibMaxofSigned(V) \
198+
/* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \
199+
mlib_bits(mlib_bitsizeof(V) - 1u, 0)
204200
// Generates an 0b10000... bit pattern for the two's complement min value:
205-
#define _mlibMinofSigned(V) ((0 & (V)) - (UINTMAX_C (1) << ((sizeof (V) * CHAR_BIT) - 1)))
201+
#define _mlibMinofSigned(V) \
202+
/* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \
203+
(0 - mlib_bits(1, mlib_bitsizeof(V) - 1u))
206204
// For completeness:
207205
#define _mlibMinofUnsigned(V) 0
208206
// Yields true iff the operand expression has a signed type, but requires that
@@ -265,7 +263,7 @@ static inline bool (mlib_add) (uintmax_t *dst, bool dst_signed, bool a_signed, u
265263
// Perform regular wrapping arithmetic on the unsigned value. The bit pattern
266264
// is equivalent if there is two's complement signed arithmetic.
267265
const uintmax_t sum = *dst = a + b;
268-
const uintmax_t signbit = (UINTMAX_C (1) << ((sizeof (intmax_t) * CHAR_BIT) - 1));
266+
const uintmax_t signbit = mlib_bits (1, mlib_bitsizeof (uintmax_t) - 1u);
269267
// Now we check whether that overflowed according to the sign configuration.
270268
// We use some bit fiddling magic that treat the signbit as a boolean for
271269
// "is this number negative?" or "is this number “large” (i.e. bigger than signed-max)?"
@@ -359,7 +357,7 @@ static inline bool (mlib_sub) (uintmax_t *dst, bool dst_signed, bool a_signed, u
359357
{
360358
// Perform the subtraction using regular wrapping arithmetic
361359
const uintmax_t diff = *dst = a - b;
362-
const uintmax_t signbit = (UINTMAX_C (1) << ((sizeof (intmax_t) * CHAR_BIT) - 1));
360+
const uintmax_t signbit = mlib_bits (1, mlib_bitsizeof (uintmax_t) - 1u);
363361
// Test whether the operation overflowed for the given sign configuration
364362
// (See mlib_add for more details on why we do this bit fiddling)
365363
if (dst_signed) {
@@ -460,7 +458,7 @@ static inline bool (mlib_mul) (uintmax_t *dst, bool dst_signed, bool a_signed, u
460458
mlib_noexcept
461459
{
462460
// Multiplication is a lot more subtle
463-
const uintmax_t signbit = (UINTMAX_C (1) << ((sizeof (intmax_t) * CHAR_BIT) - 1));
461+
const uintmax_t signbit = mlib_bits (1, mlib_bitsizeof (uintmax_t) - 1u);
464462
if (dst_signed) {
465463
if (a_signed) {
466464
if (b_signed) {
@@ -576,7 +574,7 @@ _mlib_ckdint (void *dst,
576574
{
577575
// Perform the arithmetic on uintmax_t, for wrapping behavior
578576
uintmax_t tmp;
579-
bool ovr = fn (&tmp, minval < 0, a.is_signed, a.i.u, b.is_signed, b.i.u);
577+
bool ovr = fn (&tmp, minval < 0, a.is_signed, a.bits.as_unsigned, b.is_signed, b.bits.as_unsigned);
580578
// Endian-adjusting for writing the result
581579
const char *copy_from = (const char *) &tmp;
582580
if (!mlib_is_little_endian ()) {
@@ -651,7 +649,7 @@ _mlib_checked_cast (intmax_t min_,
651649
here.lineno,
652650
here.func,
653651
expr,
654-
(long long) val.i.s,
652+
(long long) val.bits.as_signed,
655653
typename_);
656654
} else {
657655
fprintf (stderr,
@@ -660,16 +658,16 @@ _mlib_checked_cast (intmax_t min_,
660658
here.lineno,
661659
here.func,
662660
expr,
663-
(unsigned long long) val.i.u,
661+
(unsigned long long) val.bits.as_unsigned,
664662
typename_);
665663
}
666664
fflush (stderr);
667665
abort ();
668666
}
669667
if (val.is_signed) {
670-
return (uintmax_t) val.i.s;
668+
return (uintmax_t) val.bits.as_signed;
671669
}
672-
return val.i.u;
670+
return val.bits.as_unsigned;
673671
}
674672

675673
mlib_extern_c_end ();

src/common/src/mlib/ckdint.test.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
#endif // __has_builtin
1616
#endif
1717

18-
static_assert (mlib_upsize_integer (42ll).is_signed, "mlib_upsize_integer yielded the wrong answer");
19-
static_assert (!mlib_upsize_integer (42ull).is_signed, "mlib_upsize_integer yielded the wrong answer");
20-
2118
template <typename... Ts> struct typelist {
2219
};
2320
using integer_types = typelist<char,

src/common/src/mlib/cmp.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,32 +77,32 @@ mlib_always_inline static enum mlib_cmp_result (mlib_cmp) (struct mlib_upsized_i
7777
if (x.is_signed) {
7878
if (y.is_signed) {
7979
// Both signed
80-
if (x.i.s < y.i.s) {
80+
if (x.bits.as_signed < y.bits.as_signed) {
8181
return mlib_less;
82-
} else if (x.i.s > y.i.s) {
82+
} else if (x.bits.as_signed > y.bits.as_signed) {
8383
return mlib_greater;
8484
}
8585
} else {
8686
// X signed, Y unsigned
87-
if (x.i.s < 0 || (uintmax_t) x.i.s < y.i.u) {
87+
if (x.bits.as_signed < 0 || (uintmax_t) x.bits.as_signed < y.bits.as_unsigned) {
8888
return mlib_less;
89-
} else if ((uintmax_t) x.i.s > y.i.u) {
89+
} else if ((uintmax_t) x.bits.as_signed > y.bits.as_unsigned) {
9090
return mlib_greater;
9191
}
9292
}
9393
} else {
9494
if (!y.is_signed) {
9595
// Both unsigned
96-
if (x.i.u < y.i.u) {
96+
if (x.bits.as_unsigned < y.bits.as_unsigned) {
9797
return mlib_less;
98-
} else if (x.i.u > y.i.u) {
98+
} else if (x.bits.as_unsigned > y.bits.as_unsigned) {
9999
return mlib_greater;
100100
}
101101
} else {
102102
// X unsigned, Y signed
103-
if (y.i.s < 0 || x.i.u > (uintmax_t) y.i.s) {
103+
if (y.bits.as_signed < 0 || x.bits.as_unsigned > (uintmax_t) y.bits.as_signed) {
104104
return mlib_greater;
105-
} else if (x.i.u < (uintmax_t) y.i.s) {
105+
} else if (x.bits.as_unsigned < (uintmax_t) y.bits.as_signed) {
106106
return mlib_less;
107107
}
108108
}
@@ -123,9 +123,9 @@ mlib_always_inline static enum mlib_cmp_result (mlib_cmp) (struct mlib_upsized_i
123123
static inline bool (mlib_in_range) (intmax_t min_, uintmax_t max_, struct mlib_upsized_integer val) mlib_noexcept
124124
{
125125
if (val.is_signed) {
126-
return mlib_cmp (val.i.s, >=, min_) && mlib_cmp (val.i.s, <=, max_);
126+
return mlib_cmp (val.bits.as_signed, >=, min_) && mlib_cmp (val.bits.as_signed, <=, max_);
127127
} else {
128-
return mlib_cmp (val.i.u, >=, min_) && mlib_cmp (val.i.u, <=, max_);
128+
return mlib_cmp (val.bits.as_unsigned, >=, min_) && mlib_cmp (val.bits.as_unsigned, <=, max_);
129129
}
130130
}
131131

src/common/src/mlib/intutil.h

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,59 @@
3232
*/
3333
#define mlib_is_signed(T) (!((T) (-1) > 0))
3434

35+
/**
36+
* @brief Like `sizeof`, but returns the number of bits in the object representation
37+
*/
38+
#define mlib_bitsizeof(T) ((sizeof (T)) * ((size_t) CHAR_BIT))
39+
3540
// clang-format off
41+
/**
42+
* @brief Generate a mask of contiguous bits.
43+
*
44+
* @param NumOnes The non-negative number of contiguous 1 bits
45+
* @param NumZeros The non-negative number of contiguous 0 bits to set in the low position
46+
*
47+
* The generated mask is of the form:
48+
*
49+
* NumZeros
50+
* │
51+
* ┌┴─┐
52+
* │ │
53+
* `0..0 1..1 0..0`
54+
* │ │
55+
* └┬─┘
56+
* │
57+
* NumOnes
58+
*
59+
* Explain the arithmetic below:
60+
*
61+
* 1. `ones = 0b1111...` : All high bits
62+
* 2. `tmp = ones >> (NumOnes - num_bits_of(ones))` : Truncate to the number of 1s we want
63+
* 3. `res = tmp << NumZeros` : Add the 0s in the low position
64+
*/
65+
#define mlib_bits(NumOnes, NumZeros) ( \
66+
((NumOnes) \
67+
? (~UINTMAX_C(0) >> ((mlib_bitsizeof(uintmax_t) - (uintmax_t)(NumOnes)))) \
68+
: 0) \
69+
<< ((uintmax_t)(NumZeros)))
70+
3671
/**
3772
* @brief Given an integral type, yield an integral constant value representing
3873
* the maximal value of that type.
3974
*/
4075
#define mlib_maxof(T) \
4176
((T) (mlib_is_signed (T) \
42-
? ((T) ((((T) 1 << (sizeof (T) * CHAR_BIT - 2)) - 1) * 2 + 1)) \
43-
: ((T) ~(T) 0)))
77+
? ((T) mlib_bits(mlib_bitsizeof(T) - 1u, 0)) \
78+
: ((T) mlib_bits(mlib_bitsizeof(T), 0))))
4479

4580
/**
4681
* @brief Given an integral type, yield an integral constant value for the
4782
* minimal value of that type.
4883
*/
4984
#define mlib_minof(T) \
50-
MLIB_PRAGMA_IF_MSVC (warning (push)) \
51-
MLIB_PRAGMA_IF_MSVC (warning (disable : 4146)) \
5285
((T) (!mlib_is_signed (T) \
5386
? (T) 0 \
54-
: (T) (-((((T) 1 << (sizeof (T) * CHAR_BIT - 2)) - 1) * 2 + 1) - 1))) \
55-
MLIB_PRAGMA_IF_MSVC (warning (pop))
87+
: (T) mlib_bits(1, mlib_bitsizeof(T) - 1u)))
5688
// clang-format on
5789

5890
/**
@@ -63,11 +95,11 @@
6395
typedef struct mlib_upsized_integer {
6496
union {
6597
// The signed value of the integer
66-
intmax_t s;
98+
intmax_t as_signed;
6799
// The unsigned value of the integer
68-
uintmax_t u;
69-
} i;
70-
// Whether the upscaled integer is stored in the signed field or the unsigned field
100+
uintmax_t as_unsigned;
101+
} bits;
102+
// Whether the upscaled integer bits should be treated as a two's complement signed integer
71103
bool is_signed;
72104
} mlib_upsized_integer;
73105

@@ -85,23 +117,29 @@ typedef struct mlib_upsized_integer {
85117
* If the integer to upcast is the same size as `intmax_t`, we need to decide whether to store
86118
* it as unsigned. The expression `(_mlibGetOne(Value)) - 2 < 1` will be `true` iff the operand is signed,
87119
* otherwise false. If the operand is signed, we can safely cast to `intmax_t` (it probably already
88-
* is of that type), otherwise, we can to `uintmax_t` and the returned `mlib_upsized_integer` will
120+
* is of that type), otherwise, we cast to `uintmax_t` and the returned `mlib_upsized_integer` will
89121
* indicate that the stored value is unsigned. The expression `1 - 2 < 1` is chosen
90122
* to avoid `-Wtype-limits` warnings from some compilers about unsigned comparison.
91123
*/
92124
#define mlib_upsize_integer(Value) \
93-
MLIB_PRAGMA_IF_MSVC (warning(push)) \
94-
MLIB_PRAGMA_IF_MSVC (warning(disable : 4189)) \
125+
mlib_upsize_integer((uintmax_t)(intmax_t)((Value)), _mlibShouldTreatBitsAsSigned(Value))
126+
#define _mlibShouldTreatBitsAsSigned(Value) \
95127
/* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \
96-
((sizeof ((Value)) < sizeof (intmax_t) || (_mlibGetOne(Value) - 2) < _mlibGetOne(Value)) \
97-
? mlib_init(mlib_upsized_integer) {{(intmax_t) (Value)}, true} \
98-
: mlib_init(mlib_upsized_integer) {{(intmax_t) (uintmax_t) (Value)}}) \
99-
MLIB_PRAGMA_IF_MSVC (warning(pop))
128+
(sizeof ((Value)) < sizeof (intmax_t) || (_mlibGetOne(Value) - 2) < _mlibGetOne(Value))
100129
// Yield a 1 value of similar-ish type to the given expression. The ternary
101130
// forces an integer promotion of literal 1 match the type of `V`, while leaving
102131
// `V` unevaluated. Note that this will also promote `V` to be at least `(unsigned) int`,
103132
// so the 1 value is only "similar" to `V`, and may be of a larger type
104133
#define _mlibGetOne(V) (1 ? 1 : (V))
134+
// Function impl for upsize_integer
135+
static inline mlib_upsized_integer
136+
(mlib_upsize_integer) (uintmax_t bits, bool treat_as_signed)
137+
{
138+
mlib_upsized_integer ret;
139+
ret.bits.as_unsigned = bits;
140+
ret.is_signed = treat_as_signed;
141+
return ret;
142+
}
105143
// clang-format on
106144

107145
#endif // MLIB_INTUTIL_H_INCLUDED

src/common/src/mlib/str.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,16 @@ _mstr_adjust_index (mstr_view s, mlib_upsized_integer pos, bool clamp_to_length)
276276
// We want to clamp to the length, and the given value is greater than the string length.
277277
return s.len;
278278
}
279-
if (pos.is_signed && pos.i.s < 0) {
279+
if (pos.is_signed && pos.bits.as_signed < 0) {
280280
// This will add the negative value to the length of the string. If such
281281
// an operation would result a negative value, this will terminate the
282282
// program.
283-
return mlib_assert_add (size_t, s.len, pos.i.s);
283+
return mlib_assert_add (size_t, s.len, pos.bits.as_signed);
284284
}
285285
// No special behavior, just assert that the given position is in-bounds for the string
286-
mlib_check (pos.i.u <= s.len, because, "the string position index must not be larger than the string length");
287-
return pos.i.u;
286+
mlib_check (
287+
pos.bits.as_unsigned <= s.len, because, "the string position index must not be larger than the string length");
288+
return pos.bits.as_unsigned;
288289
}
289290

290291
/**

src/common/src/mlib/test.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,16 @@ _mlibCheckIntCmp (enum mlib_cmp_result cres, // The cmp result to check
193193
right_expr);
194194
fprintf (stderr, " ");
195195
if (left.is_signed) {
196-
fprintf (stderr, "%lld", (long long) left.i.s);
196+
fprintf (stderr, "%lld", (long long) left.bits.as_signed);
197197
} else {
198-
fprintf (stderr, "%llu", (unsigned long long) left.i.u);
198+
fprintf (stderr, "%llu", (unsigned long long) left.bits.as_unsigned);
199199
}
200200
fprintf (stderr, " ⟨%s⟩\n", left_expr);
201201
fprintf (stderr, " ");
202202
if (right.is_signed) {
203-
fprintf (stderr, "%lld", (long long) right.i.s);
203+
fprintf (stderr, "%lld", (long long) right.bits.as_signed);
204204
} else {
205-
fprintf (stderr, "%llu", (unsigned long long) right.i.u);
205+
fprintf (stderr, "%llu", (unsigned long long) right.bits.as_unsigned);
206206
}
207207
fprintf (stderr, " ⟨%s⟩\n", right_expr);
208208
fflush (stderr);

src/common/tests/test-mlib.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ _test_checks (void)
5252
}
5353
}
5454

55+
static void
56+
_test_bits (void)
57+
{
58+
mlib_check (mlib_bits (0, 0), eq, 0); // 0b000
59+
mlib_check (mlib_bits (1, 0), eq, 1); // 0b001
60+
mlib_check (mlib_bits (2, 0), eq, 3); // 0b011
61+
mlib_check (mlib_bits (1, 1), eq, 2); // 0b010
62+
mlib_check (mlib_bits (5, 3), eq, 248); // 0b11111000
63+
mlib_check (mlib_bits (64, 0), eq, UINT64_MAX); // 0b111...
64+
}
65+
5566
static void
5667
_test_minmax (void)
5768
{
@@ -95,23 +106,23 @@ _test_upsize (void)
95106
{
96107
struct mlib_upsized_integer up;
97108
up = mlib_upsize_integer (31);
98-
ASSERT (up.is_signed);
99-
ASSERT (up.i.s == 31);
109+
mlib_check (up.is_signed);
110+
mlib_check (up.bits.as_signed == 31);
100111

101112
// Casting from the max unsigned integer generates an unsigned upsized integer:
102113
up = mlib_upsize_integer ((uintmax_t) 1729);
103-
ASSERT (!up.is_signed);
104-
ASSERT (up.i.u == 1729);
114+
mlib_check (!up.is_signed);
115+
mlib_check (up.bits.as_unsigned == 1729);
105116

106117
// Max signed integer makes a signed upsized integer:
107118
up = mlib_upsize_integer ((intmax_t) 1729);
108-
ASSERT (up.is_signed);
109-
ASSERT (up.i.s == 1729);
119+
mlib_check (up.is_signed);
120+
mlib_check (up.bits.as_signed == 1729);
110121

111122
// From a literal:
112123
up = mlib_upsize_integer (UINTMAX_MAX);
113-
ASSERT (!up.is_signed);
114-
ASSERT (up.i.u == UINTMAX_MAX);
124+
mlib_check (!up.is_signed);
125+
mlib_check (up.bits.as_unsigned == UINTMAX_MAX);
115126
}
116127

117128
static void
@@ -880,6 +891,7 @@ void
880891
test_mlib_install (TestSuite *suite)
881892
{
882893
TestSuite_Add (suite, "/mlib/checks", _test_checks);
894+
TestSuite_Add (suite, "/mlib/intutil/bits", _test_bits);
883895
TestSuite_Add (suite, "/mlib/intutil/minmax", _test_minmax);
884896
TestSuite_Add (suite, "/mlib/intutil/upsize", _test_upsize);
885897
TestSuite_Add (suite, "/mlib/cmp", _test_cmp);

0 commit comments

Comments
 (0)