summaryrefslogtreecommitdiffstats
path: root/src/int.h
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-05-16 10:24:29 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-05-16 11:29:48 -0400
commit6b0209f04a7a2fd3f8aec78808225177647c3aec (patch)
treecc2220c41a339cf985537b1a803226c99cde677f /src/int.h
parentc13171fd7177c843e2e08417297babf99a365f1c (diff)
downloadbfs-6b0209f04a7a2fd3f8aec78808225177647c3aec.tar.xz
int: Backport C23's _WIDTH macros
Diffstat (limited to 'src/int.h')
-rw-r--r--src/int.h100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/int.h b/src/int.h
new file mode 100644
index 0000000..56fabee
--- /dev/null
+++ b/src/int.h
@@ -0,0 +1,100 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/**
+ * Bits & bytes.
+ */
+
+#ifndef BFS_INT_H
+#define BFS_INT_H
+
+#include <limits.h>
+#include <stdint.h>
+
+// C23 polyfill: _WIDTH macros
+
+// The U*_MAX macros are of the form 2**n - 1, and we want to extract the n.
+// One way would be *_WIDTH = popcount(*_MAX). Alternatively, we can use
+// Hallvard B. Furuseth's technique from [1], which is shorter.
+//
+// [1]: https://groups.google.com/g/comp.lang.c/c/NfedEFBFJ0k
+
+// Let mask be of the form 2**m - 1, e.g. 0b111, and let n range over
+// [0b0, 0b1, 0b11, 0b111, 0b1111, ...]. Then we have
+//
+// n % 0b111
+// == [0b0, 0b1, 0b11, 0b0, 0b1, 0b11, ...]
+// n / (n % 0b111 + 1)
+// == [0b0 (x3), 0b111 (x3), 0b111111 (x3), ...]
+// n / (n % 0b111 + 1) / 0b111
+// == [0b0 (x3), 0b1 (x3), 0b1001 (x3), 0b1001001 (x3), ...]
+// n / (n % 0b111 + 1) / 0b111 % 0b111
+// == [0 (x3), 1 (x3), 2 (x3), ...]
+// == UMAX_CHUNK(n, 0b111)
+#define UMAX_CHUNK(n, mask) (n / (n % mask + 1) / mask % mask)
+
+// 8 * UMAX_CHUNK(n, 255) gives [0 (x8), 8 (x8), 16 (x8), ...]. To that we add
+// [0, 1, 2, ..., 6, 7, 0, 1, ...], which we get from a linear interpolation on
+// n % 255:
+//
+// n % 255
+// == [0, 1, 3, 7, 15, 31, 63, 127, 0, ...]
+// 86 / (n % 255 + 12)
+// == [7, 6, 5, 4, 3, 2, 1, 0, 7, ...]
+#define UMAX_INTERP(n) (7 - 86 / (n % 255 + 12))
+
+#define UMAX_WIDTH(n) (8 * UMAX_CHUNK(n, 255) + UMAX_INTERP(n))
+
+#ifndef CHAR_WIDTH
+# define CHAR_WIDTH CHAR_BIT
+#endif
+#ifndef UCHAR_WIDTH
+# define UCHAR_WIDTH CHAR_WIDTH
+#endif
+#ifndef SCHAR_WIDTH
+# define SCHAR_WIDTH CHAR_WIDTH
+#endif
+#ifndef USHRT_WIDTH
+# define USHRT_WIDTH UMAX_WIDTH(USHRT_MAX)
+#endif
+#ifndef SHRT_WIDTH
+# define SHRT_WIDTH USHRT_WIDTH
+#endif
+#ifndef UINT_WIDTH
+# define UINT_WIDTH UMAX_WIDTH(UINT_MAX)
+#endif
+#ifndef INT_WIDTH
+# define INT_WIDTH UINT_WIDTH
+#endif
+#ifndef ULONG_WIDTH
+# define ULONG_WIDTH UMAX_WIDTH(ULONG_MAX)
+#endif
+#ifndef LONG_WIDTH
+# define LONG_WIDTH ULONG_WIDTH
+#endif
+#ifndef ULLONG_WIDTH
+# define ULLONG_WIDTH UMAX_WIDTH(ULLONG_MAX)
+#endif
+#ifndef LLONG_WIDTH
+# define LLONG_WIDTH ULLONG_WIDTH
+#endif
+#ifndef SIZE_WIDTH
+# define SIZE_WIDTH UMAX_WIDTH(SIZE_MAX)
+#endif
+#ifndef PTRDIFF_WIDTH
+# define PTRDIFF_WIDTH (UMAX_WIDTH(PTRDIFF_MAX) + 1)
+#endif
+#ifndef UINTPTR_WIDTH
+# define UINTPTR_WIDTH UMAX_WIDTH(UINTPTR_MAX)
+#endif
+#ifndef INTPTR_WIDTH
+# define INTPTR_WIDTH UINTPTR_WIDTH
+#endif
+#ifndef UINTMAX_WIDTH
+# define UINTMAX_WIDTH UMAX_WIDTH(UINTMAX_MAX)
+#endif
+#ifndef INTMAX_WIDTH
+# define INTMAX_WIDTH UINTMAX_WIDTH
+#endif
+
+#endif // BFS_INT_H