#![allow(clippy::uninlined_format_args)]
use crate::error::Result;
use crate::std::str;
use crate::{fmt, ops, raw};
#[repr(transparent)]
#[derive(PartialEq, Eq)]
pub struct Name([u8]);
pub trait AsName {
fn name(&self) -> &Name;
}
impl AsName for [u8] {
fn name(&self) -> &Name {
assert!(
!self.is_empty(),
"cannot create Name from empty byte-string"
);
assert_eq!(
*self.last().unwrap(),
b'\0',
"cannot create Name from non-null-terminated byte-string \"{}\"",
str::from_utf8(self).unwrap()
);
unsafe { &*(self as *const Self as *const Name) }
}
}
impl AsName for str {
fn name(&self) -> &Name {
self.as_bytes().name()
}
}
impl Name {
pub fn mib<T: MibArg>(&self) -> Result<Mib<T>> {
let mut mib: Mib<T> = Mib::default();
raw::name_to_mib(&self.0, mib.0.as_mut())?;
Ok(mib)
}
pub fn mib_str<T: MibArg>(&self) -> Result<MibStr<T>> {
assert!(
self.value_type_str(),
"key \"{}\" does not refer to a string",
self
);
let mut mib: MibStr<T> = MibStr::default();
raw::name_to_mib(&self.0, mib.0.as_mut())?;
Ok(mib)
}
pub fn value_type_str(&self) -> bool {
let name = self.0.split_at(self.0.len() - 1).0;
if name.is_empty() {
return false;
}
debug_assert_ne!(*name.last().unwrap(), b'\0');
match name {
b"version"
| b"config.malloc_conf"
| b"opt.metadata_thp"
| b"opt.dss"
| b"opt.percpu_arena"
| b"opt.stats_print_opts"
| b"opt.junk"
| b"opt.thp"
| b"opt.prof_prefix"
| b"thread.prof.name"
| b"prof.dump" => true,
v if v.starts_with(b"arena.") && v.ends_with(b".dss") => true,
v if v.starts_with(b"stats.arenas.") && v.ends_with(b".dss") => {
true
}
_ => false,
}
}
pub fn as_bytes(&self) -> &'static [u8] {
unsafe { &*(self as *const Self as *const [u8]) }
}
}
impl fmt::Debug for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", str::from_utf8(&self.0).unwrap())
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", str::from_utf8(&self.0).unwrap())
}
}
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct Mib<T: MibArg>(T);
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct MibStr<T: MibArg>(T);
impl<T: MibArg> AsRef<[usize]> for Mib<T> {
fn as_ref(&self) -> &[usize] {
self.0.as_ref()
}
}
impl<T: MibArg> AsMut<[usize]> for Mib<T> {
fn as_mut(&mut self) -> &mut [usize] {
self.0.as_mut()
}
}
impl<T: MibArg> ops::Index<usize> for Mib<T> {
type Output = usize;
fn index(&self, idx: usize) -> &Self::Output {
&self.0.as_ref()[idx]
}
}
impl<T: MibArg> ops::IndexMut<usize> for Mib<T> {
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
&mut self.0.as_mut()[idx]
}
}
impl<T: MibArg> ops::Index<usize> for MibStr<T> {
type Output = usize;
fn index(&self, idx: usize) -> &Self::Output {
&self.0.as_ref()[idx]
}
}
impl<T: MibArg> ops::IndexMut<usize> for MibStr<T> {
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
&mut self.0.as_mut()[idx]
}
}
pub trait Access<T> {
fn read(&self) -> Result<T>;
fn write(&self, value: T) -> Result<()>;
fn update(&self, value: T) -> Result<T>;
}
macro_rules! impl_access {
($id:ty) => {
impl<T: MibArg> Access<$id> for Mib<T> {
fn read(&self) -> Result<$id> {
unsafe { raw::read_mib(self.0.as_ref()) }
}
fn write(&self, value: $id) -> Result<()> {
unsafe { raw::write_mib(self.0.as_ref(), value) }
}
fn update(&self, value: $id) -> Result<$id> {
unsafe { raw::update_mib(self.0.as_ref(), value) }
}
}
impl Access<$id> for Name {
fn read(&self) -> Result<$id> {
unsafe { raw::read(&self.0) }
}
fn write(&self, value: $id) -> Result<()> {
unsafe { raw::write(&self.0, value) }
}
fn update(&self, value: $id) -> Result<$id> {
unsafe { raw::update(&self.0, value) }
}
}
};
}
impl_access!(u32);
impl_access!(u64);
impl_access!(isize);
impl_access!(usize);
impl<T: MibArg> Access<bool> for Mib<T> {
fn read(&self) -> Result<bool> {
unsafe {
let v: u8 = raw::read_mib(self.0.as_ref())?;
assert!(v == 0 || v == 1);
Ok(v == 1)
}
}
fn write(&self, value: bool) -> Result<()> {
unsafe { raw::write_mib(self.0.as_ref(), value) }
}
fn update(&self, value: bool) -> Result<bool> {
unsafe {
let v: u8 = raw::update_mib(self.0.as_ref(), value as u8)?;
Ok(v == 1)
}
}
}
impl Access<bool> for Name {
fn read(&self) -> Result<bool> {
unsafe {
let v: u8 = raw::read(&self.0)?;
assert!(v == 0 || v == 1);
Ok(v == 1)
}
}
fn write(&self, value: bool) -> Result<()> {
unsafe { raw::write(&self.0, value) }
}
fn update(&self, value: bool) -> Result<bool> {
unsafe {
let v: u8 = raw::update(&self.0, value as u8)?;
Ok(v == 1)
}
}
}
impl<T: MibArg> Access<&'static [u8]> for MibStr<T> {
fn read(&self) -> Result<&'static [u8]> {
unsafe { raw::read_str_mib(self.0.as_ref()) }
}
fn write(&self, value: &'static [u8]) -> Result<()> {
raw::write_str_mib(self.0.as_ref(), value)
}
fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> {
unsafe { raw::update_str_mib(self.0.as_ref(), value) }
}
}
impl Access<&'static [u8]> for Name {
fn read(&self) -> Result<&'static [u8]> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
unsafe { raw::read_str(&self.0) }
}
fn write(&self, value: &'static [u8]) -> Result<()> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
raw::write_str(&self.0, value)
}
fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
unsafe { raw::update_str(&self.0, value) }
}
}
impl<T: MibArg> Access<&'static str> for MibStr<T> {
fn read(&self) -> Result<&'static str> {
let s = unsafe { raw::read_str_mib(self.0.as_ref())? };
Ok(str::from_utf8(s).unwrap())
}
fn write(&self, value: &'static str) -> Result<()> {
raw::write_str_mib(self.0.as_ref(), value.as_bytes())
}
fn update(&self, value: &'static str) -> Result<&'static str> {
let s =
unsafe { raw::update_str_mib(self.0.as_ref(), value.as_bytes())? };
Ok(str::from_utf8(s).unwrap())
}
}
impl Access<&'static str> for Name {
fn read(&self) -> Result<&'static str> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
let s = unsafe { raw::read_str(&self.0)? };
Ok(str::from_utf8(s).unwrap())
}
fn write(&self, value: &'static str) -> Result<()> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
raw::write_str(&self.0, value.as_bytes())
}
fn update(&self, value: &'static str) -> Result<&'static str> {
assert!(
self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string",
self
);
let s = unsafe { raw::update_str(&self.0, value.as_bytes())? };
Ok(str::from_utf8(s).unwrap())
}
}
#[cfg(test)]
mod tests {
use super::{Access, AsName, Mib, MibStr};
#[test]
fn bool_rw() {
let name = b"thread.tcache.enabled\0".name();
let tcache: bool = name.read().unwrap();
let new_tcache = !tcache;
name.write(new_tcache).unwrap();
let mib: Mib<[usize; 3]> = name.mib().unwrap();
let r: bool = mib.read().unwrap();
assert_eq!(r, new_tcache);
}
#[test]
fn u32_r() {
let name = b"arenas.bin.0.nregs\0".name();
let v: u32 = name.read().unwrap();
let mib: Mib<[usize; 4]> = name.mib().unwrap();
let r: u32 = mib.read().unwrap();
assert_eq!(r, v);
}
#[test]
fn size_t_r() {
let name = b"arenas.lextent.0.size\0".name();
let v: libc::size_t = name.read().unwrap();
let mib: Mib<[usize; 4]> = name.mib().unwrap();
let r: libc::size_t = mib.read().unwrap();
assert_eq!(r, v);
}
#[test]
fn ssize_t_rw() {
let name = b"arenas.dirty_decay_ms\0".name();
let v: libc::ssize_t = name.read().unwrap();
name.write(v).unwrap();
let mib: Mib<[usize; 2]> = name.mib().unwrap();
let r: libc::ssize_t = mib.read().unwrap();
assert_eq!(r, v);
}
#[test]
fn u64_rw() {
let name = b"epoch\0".name();
let epoch: u64 = name.read().unwrap();
name.write(epoch).unwrap();
let mib: Mib<[usize; 1]> = name.mib().unwrap();
let epoch: u64 = mib.read().unwrap();
mib.write(epoch).unwrap();
}
#[test]
fn str_rw() {
let name = b"arena.0.dss\0".name();
let dss: &'static [u8] = name.read().unwrap();
name.write(dss).unwrap();
let mib: MibStr<[usize; 3]> = name.mib_str().unwrap();
let dss2: &'static [u8] = mib.read().unwrap();
mib.write(dss2).unwrap();
assert_eq!(dss, dss2);
}
}
pub trait MibArg:
Copy
+ Clone
+ PartialEq
+ Default
+ fmt::Debug
+ AsRef<[usize]>
+ AsMut<[usize]>
{
}
impl<T> MibArg for T where
T: Copy
+ Clone
+ PartialEq
+ Default
+ fmt::Debug
+ AsRef<[usize]>
+ AsMut<[usize]>
{
}