Import 'libbpf-rs' crate v0.24.2 Request Document: go/android-rust-importing-crates For CL Reviewers: go/android3p#cl-review For Build Team: go/ab-third-party-imports Bug: 360116764 Bug: 359646531 Test: none Change-Id: I567d104d028eeb63a1b9f3bfd57c60d93ed48ffb Signed-off-by: Neill Kapron <[email protected]>
diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..c304db5 --- /dev/null +++ b/src/util.rs
@@ -0,0 +1,168 @@ +use std::ffi::CStr; +use std::ffi::CString; +use std::mem::transmute; +use std::ops::Deref; +use std::os::raw::c_char; +use std::path::Path; +use std::ptr::NonNull; +use std::sync::OnceLock; + +use crate::Error; +use crate::Result; + +pub fn str_to_cstring(s: &str) -> Result<CString> { + CString::new(s).map_err(|e| Error::with_invalid_data(e.to_string())) +} + +pub fn path_to_cstring<P: AsRef<Path>>(path: P) -> Result<CString> { + let path_str = path.as_ref().to_str().ok_or_else(|| { + Error::with_invalid_data(format!("{} is not valid unicode", path.as_ref().display())) + })?; + + str_to_cstring(path_str) +} + +pub fn c_ptr_to_string(p: *const c_char) -> Result<String> { + if p.is_null() { + return Err(Error::with_invalid_data("Null string")); + } + + let c_str = unsafe { CStr::from_ptr(p) }; + Ok(c_str + .to_str() + .map_err(|e| Error::with_invalid_data(e.to_string()))? + .to_owned()) +} + +/// Convert a `[c_char]` into a `CStr`. +pub fn c_char_slice_to_cstr(s: &[c_char]) -> Option<&CStr> { + // TODO: Switch to using `CStr::from_bytes_until_nul` once we require + // Rust 1.69.0. + let nul_idx = s + .iter() + .enumerate() + .find_map(|(idx, b)| (*b == 0).then_some(idx))?; + let cstr = + // SAFETY: `c_char` and `u8` are both just one byte plain old data + // types. + CStr::from_bytes_with_nul(unsafe { transmute::<&[c_char], &[u8]>(&s[0..=nul_idx]) }) + .unwrap(); + Some(cstr) +} + +/// Round up a number to the next multiple of `r` +pub fn roundup(num: usize, r: usize) -> usize { + ((num + (r - 1)) / r) * r +} + +/// Get the number of CPUs in the system, e.g., to interact with per-cpu maps. +pub fn num_possible_cpus() -> Result<usize> { + let ret = unsafe { libbpf_sys::libbpf_num_possible_cpus() }; + parse_ret(ret).map(|()| ret as usize) +} + +pub fn parse_ret(ret: i32) -> Result<()> { + if ret < 0 { + // Error code is returned negative, flip to positive to match errno + Err(Error::from_raw_os_error(-ret)) + } else { + Ok(()) + } +} + +pub fn parse_ret_i32(ret: i32) -> Result<i32> { + parse_ret(ret).map(|()| ret) +} + + +/// Check the returned pointer of a `libbpf` call, extracting any +/// reported errors and converting them. +pub fn validate_bpf_ret<T>(ptr: *mut T) -> Result<NonNull<T>> { + // SAFETY: `libbpf_get_error` is always safe to call. + match unsafe { libbpf_sys::libbpf_get_error(ptr as *const _) } { + 0 => { + debug_assert!(!ptr.is_null()); + // SAFETY: libbpf guarantees that if NULL is returned an + // error it set, so we will always end up with a + // valid pointer when `libbpf_get_error` returned 0. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + Ok(ptr) + } + err => Err(Error::from_raw_os_error(-err as i32)), + } +} + + +// Fix me, If std::sync::LazyLock is stable(https://github.com/rust-lang/rust/issues/109736). +pub(crate) struct LazyLock<T> { + cell: OnceLock<T>, + init: fn() -> T, +} + +impl<T> LazyLock<T> { + pub const fn new(f: fn() -> T) -> Self { + Self { + cell: OnceLock::new(), + init: f, + } + } +} + +impl<T> Deref for LazyLock<T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + self.cell.get_or_init(self.init) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_roundup() { + for i in 1..=256 { + let up = roundup(i, 8); + assert!(up % 8 == 0); + assert!(i <= up); + assert!(up - i < 8); + } + } + + #[test] + fn test_roundup_multiples() { + for i in (8..=256).step_by(8) { + assert_eq!(roundup(i, 8), i); + } + } + + #[test] + fn test_num_possible_cpus() { + let num = num_possible_cpus().unwrap(); + assert!(num > 0); + } + + /// Check that we can convert a `[c_char]` into a `CStr`. + #[test] + fn c_char_slice_conversion() { + let slice = []; + assert_eq!(c_char_slice_to_cstr(&slice), None); + + let slice = [0]; + assert_eq!( + c_char_slice_to_cstr(&slice).unwrap(), + CStr::from_bytes_with_nul(b"\0").unwrap() + ); + + let slice = ['a' as _, 'b' as _, 'c' as _, 0 as _]; + assert_eq!( + c_char_slice_to_cstr(&slice).unwrap(), + CStr::from_bytes_with_nul(b"abc\0").unwrap() + ); + + // Missing terminating NUL byte. + let slice = ['a' as _, 'b' as _, 'c' as _]; + assert_eq!(c_char_slice_to_cstr(&slice), None); + } +}