summaryrefslogtreecommitdiff
path: root/handy.h
diff options
context:
space:
mode:
Diffstat (limited to 'handy.h')
-rw-r--r--handy.h26
1 files changed, 21 insertions, 5 deletions
diff --git a/handy.h b/handy.h
index edc01075d5..51f79efcf3 100644
--- a/handy.h
+++ b/handy.h
@@ -1094,11 +1094,27 @@ patched there. The file as of this writing is cpan/Devel-PPPort/parts/inc/misc
#define FITS_IN_8_BITS(c) (1)
#endif
-/* Returns true if c is in the range l..u
- * Written with the cast so it only needs one conditional test
- */
-#define inRANGE(c, l, u) (__ASSERT_((u) >= (l)) \
- ((WIDEST_UTYPE) (((c) - (l)) | 0) <= ((WIDEST_UTYPE) ((u) - (l)))))
+/* Returns true if c is in the range l..u, where 'l' is non-negative
+ * Written this way so that after optimization, only one conditional test is
+ * needed.
+ *
+ * This isn't fully general, except for the special cased 'signed char' (which
+ * should be resolved at compile time): It won't work if 'c' is negative, and
+ * 'l' is larger than the max for that signed type. Thus if 'c' is a negative
+ * int, and 'l' is larger than INT_MAX, it will fail. To protect agains this
+ * happening, there is an assert that will generate a warning if c is larger
+ * than e.g. INT_MAX if it is an 'unsigned int'. This could be a false
+ * positive, but khw couldn't figure out a way to make it better. It's good
+ * enough so far */
+#define inRANGE(c, l, u) (__ASSERT_((l) >= 0) __ASSERT_((u) >= (l)) \
+ ((sizeof(c) == 1) \
+ ? (((WIDEST_UTYPE) ((((U8) (c))|0) - (l))) <= ((WIDEST_UTYPE) ((u) - (l)))) \
+ : (__ASSERT_( (((WIDEST_UTYPE) 1) << (CHARBITS * sizeof(c) - 1) & (c)) \
+ /* sign bit of c is 0 */ == 0 \
+ || (((~ ((WIDEST_UTYPE) 1) << ((CHARBITS * sizeof(c) - 1) - 1))\
+ /* l not larger than largest value in c's signed type */ \
+ & ~ ((WIDEST_UTYPE) 0)) & (l)) == 0) \
+ ((WIDEST_UTYPE) (((c) - (l)) | 0) <= ((WIDEST_UTYPE) ((u) - (l)))))))
#ifdef EBCDIC
# ifndef _ALL_SOURCE