Skip to content

Commit 209bfe3

Browse files
committed
handy.h: Avoid UB in nBIT_MASK()
I discovered the hard way that this is undefined behavior when operating on the widest unsigned integer type available on the platform. I couldn't think of a way to write this without a branch that worked both for that condition and a zero length mask
1 parent 8ac8d05 commit 209bfe3

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

handy.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,15 @@ don't, so that you can portably take advantage of this C99 feature.
342342
# define isPOWER_OF_2(n) ((n) && ((n) & ((n)-1)) == 0)
343343
#endif
344344

345-
/* Returns a mask with the lowest n bits set */
346-
#define nBIT_MASK(n) ((UINTMAX_C(1) << (n)) - 1)
345+
/* Returns a mask with the lowest n bits set. Avoids undefined behavior if n
346+
* is, say, 64 on a 64-bit system, by:
347+
* 1) shifting only 63 yielding 1000000...000
348+
* 2) subtracting 1, yielding 0111111...111
349+
* 3) shifting by 1, yielding 1111111...110
350+
* 4) adding the final bit yielding all 1's */
351+
#define nBIT_MASK(n) \
352+
(((n) == 0) ? 0 : (((UINTMAX_C(1) << ((n) - 1)) - 1) << 1) | 1)
353+
// |_________________________|
347354

348355
/* The largest unsigned number that will fit into n bits */
349356
#define nBIT_UMAX(n) nBIT_MASK(n)

0 commit comments

Comments
 (0)