[PATCH 4/9] rust: xxhash: add a safe xxh64 wrapper

Mike Lothian <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Expose xxh64() through a slice-based Rust API for non-cryptographic
hashing such as framebuffer damage detection.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <[email protected]>
---
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/lib.rs              |  1 +
 rust/kernel/xxhash.rs           | 68 +++++++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+)
 create mode 100644 rust/kernel/xxhash.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..2d079f278a04 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -93,6 +93,7 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/xarray.h>
+#include <linux/xxhash.h>
 #include <trace/events/rust_sample.h>
 
 /*
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f39648246271..d7ced2a4c11f 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -141,6 +141,7 @@
 pub mod usb;
 pub mod workqueue;
 pub mod xarray;
+pub mod xxhash;
 
 #[doc(hidden)]
 pub use bindings;
diff --git a/rust/kernel/xxhash.rs b/rust/kernel/xxhash.rs
new file mode 100644
index 000000000000..fca24cddef46
--- /dev/null
+++ b/rust/kernel/xxhash.rs
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! xxHash, a fast non-cryptographic hash function.
+//!
+//! C header: [`include/linux/xxhash.h`](srctree/include/linux/xxhash.h)
+
+use core::mem::MaybeUninit;
+
+use crate::{bindings, error::to_result, prelude::*};
+
+/// Incremental xxHash64 state.
+///
+/// Use this when the input is split across several buffers. Calling
+/// [`update`](Self::update) for each buffer produces the same digest as hashing
+/// their concatenation with [`xxh64`].
+pub struct Xxh64(bindings::xxh64_state);
+
+impl Xxh64 {
+    /// Start a new hash with `seed`.
+    pub fn new(seed: u64) -> Self {
+        let mut state = MaybeUninit::uninit();
+        // SAFETY: `xxh64_reset()` initializes every byte of the state before
+        // returning.
+        unsafe {
+            bindings::xxh64_reset(state.as_mut_ptr(), seed);
+            Self(state.assume_init())
+        }
+    }
+
+    /// Add `data` to the hash.
+    pub fn update(&mut self, data: &[u8]) -> Result {
+        // SAFETY: `self.0` is initialized and exclusively borrowed; `data` is
+        // valid for reads of `data.len()` bytes.
+        to_result(unsafe { bindings::xxh64_update(&mut self.0, data.as_ptr().cast(), data.len()) })
+    }
+
+    /// Return the current digest without consuming the state.
+    pub fn digest(&self) -> u64 {
+        // SAFETY: `self.0` was initialized by `xxh64_reset()` and remains live.
+        unsafe { bindings::xxh64_digest(&self.0) }
+    }
+}
+
+/// Returns the 64-bit xxHash of `data`, starting from `seed`.
+///
+/// This is a non-cryptographic hash: use it for change detection, bucketing and similar, never for
+/// anything security-relevant.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::xxhash::xxh64;
+///
+/// // The same input and seed always produce the same hash.
+/// assert_eq!(xxh64(b"hello", 0), xxh64(b"hello", 0));
+///
+/// // Different seeds produce different hashes.
+/// assert_ne!(xxh64(b"hello", 0), xxh64(b"hello", 1));
+///
+/// // Hashing the empty slice is well-defined.
+/// let _ = xxh64(&[], 0);
+/// ```
+#[inline]
+pub fn xxh64(data: &[u8], seed: u64) -> u64 {
+    // SAFETY: `data` is a valid slice, so its pointer is valid for reads of `data.len()` bytes,
+    // and `xxh64()` only reads from it for the duration of the call.
+    unsafe { bindings::xxh64(data.as_ptr().cast(), data.len(), seed) }
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.