Add a macro for sizing anonymous or locally defined arrays.

Change-Id: If7f7c7fb7b45d9f07a729aad978b26ffd88222d6
diff --git a/src/macros.h b/src/macros.h
index be31e03..078628f 100644
--- a/src/macros.h
+++ b/src/macros.h
@@ -59,7 +59,7 @@
 // One caveat is that arraysize() doesn't accept any array of an
 // anonymous type or a type defined inside a function.  In these rare
-// cases, you have to use the unsafe ARRAYSIZE() macro below.  This is
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below.  This is
 // due to a limitation in C++'s template system.  The limitation might
 // eventually be removed, but it hasn't happened yet.
@@ -71,6 +71,46 @@
 #define arraysize(array) (sizeof(ArraySizeHelper(array)))
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions.  It's less safe than arraysize as it accepts some
+// (although not all) pointers.  Therefore, you should use arraysize
+// whenever possible.
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+// ARRAYSIZE_UNSAFE catches a few type errors.  If you see a compiler error
+//   "warning: division by zero in ..."
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element).  If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array.  Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size.  Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+#define ARRAYSIZE_UNSAFE(a) \
+  ((sizeof(a) / sizeof(*(a))) / \
+   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
 #define SIZEOF_MEMBER(t, f) sizeof(((t*) 4096)->f)
 #define OFFSETOF_MEMBER(t, f)         \