util: power-of-2, blocking types

Signed-off-by: Chris Frantz <cfrantz@google.com>
diff --git a/util/types/BUILD.bazel b/util/types/BUILD.bazel
new file mode 100644
index 0000000..407a06a
--- /dev/null
+++ b/util/types/BUILD.bazel
@@ -0,0 +1,24 @@
+# Licensed under the Apache-2.0 license
+# SPDX-License-Identifier: Apache-2.0
+
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+    name = "types",
+    srcs = [
+        "lib.rs",
+        "opcode.rs",
+        "power_of_2.rs",
+    ],
+    crate_name = "util_types",
+    edition = "2024",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@rust_crates//:zerocopy",
+    ],
+)
+
+rust_test(
+    name = "types_test",
+    crate = ":types",
+)
diff --git a/util/types/README.md b/util/types/README.md
new file mode 100644
index 0000000..c28d546
--- /dev/null
+++ b/util/types/README.md
@@ -0,0 +1,40 @@
+# util_types
+
+A collection of common utility types. This crate is `#![no_std]` and suitable for embedded development.
+
+## Types
+
+### [`Blocking`](lib.rs)
+
+A trait for blocking on notifications. Typically implemented by mechanisms that need to wait for an event or notification (e.g., an interrupt).
+
+```rust
+pub trait Blocking {
+    fn wait_for_notification(&self);
+}
+```
+
+### [`Opcode`](opcode.rs)
+
+A 32-bit IPC opcode, typically represented as a 4-character ASCII string. It wraps a `u32` and implements `zerocopy` traits (`FromBytes`, `IntoBytes`, `Immutable`) for safe serialization/deserialization.
+
+```rust
+pub struct Opcode(u32);
+
+impl Opcode {
+    pub const fn new(val: [u8; 4]) -> Self;
+}
+```
+
+### [`PowerOf2Usize`](power_of_2.rs)
+
+A wrapper around `usize` that is guaranteed to be a power of two. This guarantee allows the compiler to optimize operations (e.g., replacing division with bitwise shifts).
+
+```rust
+pub struct PowerOf2Usize(usize);
+
+impl PowerOf2Usize {
+    pub const fn new(val: usize) -> Option<Self>;
+    pub const fn get(self) -> usize;
+}
+```
diff --git a/util/types/lib.rs b/util/types/lib.rs
new file mode 100644
index 0000000..1af4db9
--- /dev/null
+++ b/util/types/lib.rs
@@ -0,0 +1,21 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+//! Common utility types.
+
+#![no_std]
+
+mod opcode;
+mod power_of_2;
+
+pub use opcode::Opcode;
+pub use power_of_2::PowerOf2Usize;
+
+/// A trait for blocking on notifications.
+///
+/// This trait is typically implemented by mechanisms that need to wait for
+/// an event or notification from another part of the system (e.g., an interrupt).
+pub trait Blocking {
+    /// Waits until a notification is received.
+    fn wait_for_notification(&self);
+}
diff --git a/util/types/opcode.rs b/util/types/opcode.rs
new file mode 100644
index 0000000..b207bd5
--- /dev/null
+++ b/util/types/opcode.rs
@@ -0,0 +1,25 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+//! IPC opcode definition.
+
+use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
+
+/// A 32-bit IPC opcode.
+///
+/// Opcodes are typically represented as 4-character ASCII strings.
+#[derive(Clone, Copy, PartialEq, Eq, FromBytes, IntoBytes, Immutable, KnownLayout)]
+pub struct Opcode(u32);
+
+impl Opcode {
+    /// Creates a new `Opcode` from a 4-byte array.
+    pub const fn new(val: [u8; 4]) -> Self {
+        Opcode(u32::from_le_bytes(val))
+    }
+}
+
+impl From<Opcode> for u32 {
+    fn from(op: Opcode) -> Self {
+        op.0
+    }
+}
diff --git a/util/types/power_of_2.rs b/util/types/power_of_2.rs
new file mode 100644
index 0000000..dd41445
--- /dev/null
+++ b/util/types/power_of_2.rs
@@ -0,0 +1,81 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+use core::hint::assert_unchecked;
+
+/// Represents a `usize` that is guaranteed to be a power-of-two. The compiler
+/// can take advantage of this fact when optimizing (for example, using bitwise
+/// arithmetic instead of division).
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
+pub struct PowerOf2Usize(usize);
+impl PowerOf2Usize {
+    // WARNING: Do not add any functions or derives (such as
+    // zerocopy::FromBytes) that make it possible to modify this value without
+    // confirming that it is still a power-of-two. As the compiler is relying on
+    // the power-of-two assertion for safety, any such changes are unsound.
+
+    /// Creates a new `PowerOf2Usize`.
+    ///
+    /// Returns `Some` if `val` is a power of two, `None` otherwise.
+    #[inline(always)]
+    pub const fn new(val: usize) -> Option<Self> {
+        if !val.is_power_of_two() {
+            return None;
+        }
+        Some(Self(val))
+    }
+
+    /// Returns the underlying `usize` value.
+    ///
+    /// This method asserts to the compiler that the value is a non-zero
+    /// power of two, which can enable certain optimizations.
+    #[inline(always)]
+    pub const fn get(self) -> usize {
+        // nosemgrep
+        unsafe {
+            // SAFETY: These assertions are safe because self.0 can only be set by
+            // Self::new, and we check for the same preconditions there.
+            // (LLVM is too stupid to realize that is_power_of_two() implies != 0)
+            assert_unchecked(self.0 != 0);
+            assert_unchecked(self.0.is_power_of_two())
+        };
+        self.0
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::PowerOf2Usize;
+
+    #[test]
+    pub fn test() {
+        assert_eq!(PowerOf2Usize::new(0), None);
+        assert_eq!(PowerOf2Usize::new(1).unwrap().get(), 1);
+        assert_eq!(PowerOf2Usize::new(2).unwrap().get(), 2);
+        assert_eq!(PowerOf2Usize::new(3), None);
+        assert_eq!(PowerOf2Usize::new(4).unwrap().get(), 4);
+        assert_eq!(PowerOf2Usize::new(5), None);
+        assert_eq!(PowerOf2Usize::new(6), None);
+        assert_eq!(PowerOf2Usize::new(7), None);
+        assert_eq!(PowerOf2Usize::new(8).unwrap().get(), 8);
+        assert_eq!(PowerOf2Usize::new(9), None);
+        assert_eq!(PowerOf2Usize::new(0x7fff_ffff), None);
+        assert_eq!(PowerOf2Usize::new(0x8000_0000).unwrap().get(), 0x8000_0000);
+        assert_eq!(PowerOf2Usize::new(0x8000_0001), None);
+        assert_eq!(PowerOf2Usize::new(0xc000_0000), None);
+        assert_eq!(PowerOf2Usize::new(0xffff_ffff), None);
+
+        #[cfg(target_pointer_width = "64")]
+        {
+            assert_eq!(PowerOf2Usize::new(0x7fff_ffff_ffff_ffff), None);
+            assert_eq!(
+                PowerOf2Usize::new(0x8000_0000_0000_0000).unwrap().get(),
+                0x8000_0000_0000_0000
+            );
+            assert_eq!(PowerOf2Usize::new(0x8000_0000_0000_0001), None);
+            assert_eq!(PowerOf2Usize::new(0xc000_0000_0000_0000), None);
+            assert_eq!(PowerOf2Usize::new(0xffff_ffff_ffff_ffff), None);
+        }
+    }
+}