use nix::errno::Errno; use nix::libc::c_long; use std::fmt::Display; use std::io; use std::mem::size_of; use crate::error::MmapError; use crate::Result; /// Wrapper around `checked_add()` that converts failures /// to `MmapError::Overflow`. pub trait CheckedOps: Sized { fn add_chk(self, rhs: Self) -> Result; fn mul_chk(self, rhs: Self) -> Result; } impl CheckedOps for usize { fn add_chk(self, rhs: Self) -> Result { self.checked_add(rhs) .ok_or_else(|| MmapError::overflowed(self, rhs, "adding")) } fn mul_chk(self, rhs: Self) -> Result { self.checked_mul(rhs) .ok_or_else(|| MmapError::overflowed(self, rhs, "multiplying")) } } impl CheckedOps for c_long { fn add_chk(self, rhs: Self) -> Result { self.checked_add(rhs) .ok_or_else(|| MmapError::overflowed(self, rhs, "adding")) } fn mul_chk(self, rhs: Self) -> Result { self.checked_mul(rhs) .ok_or_else(|| MmapError::overflowed(self, rhs, "multiplying")) } } /// A wrapper around `TryFrom`, returning `MmapError::FailedCast` on error. pub fn cast_chk(val: T, name: &str) -> Result where T: Copy + Display, U: std::convert::TryFrom, { U::try_from(val).map_err(|_| MmapError::failed_cast::(val, name)) } /// Retrieve errno(3). pub fn errno() -> i32 { // UNWRAP: This will always return `Some` when called from `last_os_error()`. io::Error::last_os_error().raw_os_error().unwrap() } /// Get the error string associated with errno(3). /// Equivalent to strerror(3). pub fn strerror(errno: i32) -> &'static str { Errno::from_i32(errno).desc() } /// Read a `u32` value from a byte slice starting from `offset`. #[inline] pub fn read_u32(buf: &[u8], offset: usize) -> Result { if let Some(slice) = buf.get(offset..offset + size_of::()) { // UNWRAP: We can safely unwrap the conversion from slice to array as we // the source and targets are constructed here with the same length. let out: &[u8; size_of::()] = slice.try_into().unwrap(); return Ok(u32::from_ne_bytes(*out)); } Err(MmapError::out_of_bounds(offset, buf.len())) } /// Read an `f64` value from a byte slice starting from `offset`. #[inline] pub fn read_f64(buf: &[u8], offset: usize) -> Result { if let Some(slice) = buf.get(offset..offset + size_of::()) { // UNWRAP: We can safely unwrap the conversion from slice to array as we // can be sure the target array has same length as the source slice. let out: &[u8; size_of::()] = slice.try_into().unwrap(); return Ok(f64::from_ne_bytes(*out)); } Err(MmapError::out_of_bounds( offset + size_of::(), buf.len(), )) } #[cfg(test)] mod test { use super::*; #[test] fn test_read_u32() { let buf = 1u32.to_ne_bytes(); assert!(matches!(read_u32(&buf, 0), Ok(1)), "index ok"); assert!(read_u32(&buf, 10).is_err(), "index out of range"); assert!( read_u32(&buf, 1).is_err(), "index in range but end out of range" ); } #[test] fn test_read_f64() { let buf = 1.00f64.to_ne_bytes(); let ok = read_f64(&buf, 0); assert!(ok.is_ok()); assert_eq!(ok.unwrap(), 1.00); assert!(read_f64(&buf, 10).is_err(), "index out of range"); assert!( read_f64(&buf, 1).is_err(), "index in range but end out of range" ); } }