pub trait PathExt
{
fn to_c_string(&self) -> CString;
fn make_file_read_write_all(&self) -> io::Result<()>;
fn make_folder_searchable_to_all(&self) -> io::Result<()>;
fn read_raw(&self) -> io::Result<Box<[u8]>>;
fn read_raw_without_line_feed(&self) -> io::Result<Box<[u8]>>;
fn read_value_if_exists<F>(&self) -> io::Result<Option<F>> where F: FromBytes, <F as FromBytes>::Error: 'static + Send + Sync + error::Error;
fn read_value<F>(&self) -> io::Result<F> where F: FromBytes, <F as FromBytes>::Error: 'static + Send + Sync + error::Error;
fn read_zero_or_one_bool(&self) -> io::Result<bool>;
fn write_value<'a>(&self, value: impl IntoLineFeedTerminatedByteString<'a>) -> io::Result<()>;
fn write_value_then_line_feed(&self, value: &[u8]) -> io::Result<()>;
fn open_file_for_writing(&self) -> io::Result<File>;
fn read_hyper_thread_or_numa_node_list<BSA: BitSetAware>(&self) -> Result<BitSet<BSA>, io::Error>;
fn read_hyper_thread_or_numa_node_list_if_exists<BSA: BitSetAware>(&self) -> Result<Option<BitSet<BSA>>, io::Error>;
fn parse_comma_separated_bit_set<BSA: BitSetAware>(&self) -> Result<BitSet<BSA>, io::Error>;
fn entries_in_folder_path<BSA: BitSetAware>(&self) -> Result<Option<BitSet<BSA>>, io::Error>;
fn memory_map_read_write<'a>(&self, offset: u64, address_hint: AddressHint, sharing: Sharing, prefault: bool, reserve_swap_space: bool, page_size_or_huge_page_size_settings: &PageSizeOrHugePageSizeSettings) -> io::Result<MappedMemory>;
}
impl PathExt for Path
{
#[inline(always)]
fn to_c_string(&self) -> CString
{
CString::new(self.as_os_str().as_bytes()).expect("Paths should not contain interior ASCII NULs")
}
#[inline(always)]
fn make_file_read_write_all(&self) -> io::Result<()>
{
#[inline(always)]
fn add_read_write_permissions(permissions: Permissions) -> Permissions
{
Permissions::from_mode(permissions.mode() | 0o666)
}
let metadata = metadata(self)?;
set_permissions(self, add_read_write_permissions(metadata.permissions()))
}
#[inline(always)]
fn make_folder_searchable_to_all(&self) -> io::Result<()>
{
#[inline(always)]
fn add_read_and_execute_permissions(permissions: Permissions) -> Permissions
{
Permissions::from_mode(permissions.mode() | 0o555)
}
let metadata = metadata(self)?;
set_permissions(self, add_read_and_execute_permissions(metadata.permissions()))
}
#[inline(always)]
fn read_raw(&self) -> io::Result<Box<[u8]>>
{
let raw = std::fs::read(self)?.into_boxed_slice();
if unlikely!(raw.is_empty())
{
Err(io_error_invalid_data("Empty file"))
}
else
{
Ok(raw)
}
}
#[inline(always)]
fn read_raw_without_line_feed(&self) -> io::Result<Box<[u8]>>
{
let mut raw = self.read_raw()?.to_vec();
let length = raw.len();
let should_be_line_feed = raw.remove(length - 1);
if unlikely!(should_be_line_feed != b'\n')
{
return Err(io_error_invalid_data("File lacks terminating line feed"));
}
Ok(raw.into_boxed_slice())
}
#[inline(always)]
fn read_value<F>(&self) -> io::Result<F> where F: FromBytes, <F as FromBytes>::Error: 'static + Send + Sync + error::Error
{
let bytes = self.read_raw_without_line_feed()?;
match F::from_bytes(&bytes)
{
Err(error) => Err(io_error_invalid_data(error)),
Ok(value) => Ok(value),
}
}
#[inline(always)]
fn read_value_if_exists<F>(&self) -> io::Result<Option<F>> where F: FromBytes, <F as FromBytes>::Error: 'static + Send + Sync + error::Error
{
let bytes = match self.read_raw_without_line_feed()
{
Ok(bytes) => bytes,
Err(error) => return if error.kind() == ErrorKind::NotFound
{
Ok(None)
}
else
{
Err(error)
}
};
match F::from_bytes(&bytes)
{
Err(error) => Err(io_error_invalid_data(error)),
Ok(value) => Ok(Some(value)),
}
}
#[inline(always)]
fn read_zero_or_one_bool(&self) -> io::Result<bool>
{
let bytes = self.read_raw_without_line_feed()?;
if unlikely!(bytes.len() != 1)
{
return Err(io_error_invalid_data("bool is not one byte long"));
}
match bytes[0]
{
b'0' => Ok(false),
b'1' => Ok(true),
_ => Err(io_error_invalid_data("bool is not 0 or 1")),
}
}
#[inline(always)]
fn write_value<'a>(&self, value: impl IntoLineFeedTerminatedByteString<'a>) -> io::Result<()>
{
let mut file = self.open_file_for_writing()?;
file.write_all(&value.into_line_feed_terminated_byte_string())
}
#[inline(always)]
fn write_value_then_line_feed(&self, value: &[u8]) -> io::Result<()>
{
let mut file = self.open_file_for_writing()?;
file.write_all(value)?;
file.write_all(&[b'\n'])
}
#[inline(always)]
fn open_file_for_writing(&self) -> io::Result<File>
{
OpenOptions::new().write(true).open(self)
}
#[inline(always)]
fn read_hyper_thread_or_numa_node_list<BSA: BitSetAware>(&self) -> Result<BitSet<BSA>, io::Error>
{
let without_line_feed = self.read_raw_without_line_feed()?;
BitSet::<BSA>::parse_linux_list_string(&without_line_feed).map_err(io_error_invalid_data)
}
#[inline(always)]
fn read_hyper_thread_or_numa_node_list_if_exists<BSA: BitSetAware>(&self) -> Result<Option<BitSet<BSA>>, io::Error>
{
let without_line_feed = match self.read_raw_without_line_feed()
{
Ok(without_line_feed) => without_line_feed,
Err(error) => return if error.kind() == ErrorKind::NotFound
{
Ok(None)
}
else
{
Err(error)
}
};
match BitSet::<BSA>::parse_linux_list_string(&without_line_feed)
{
Err(error) => Err(io_error_invalid_data(error)),
Ok(bit_set) => Ok(Some(bit_set)),
}
}
#[inline(always)]
fn parse_comma_separated_bit_set<BSA: BitSetAware>(&self) -> Result<BitSet<BSA>, io::Error>
{
let without_line_feed = self.read_raw_without_line_feed()?;
Ok(BitSet::parse_comma_separated_bit_set(&without_line_feed))
}
fn entries_in_folder_path<BSA: BitSetAware>(&self) -> Result<Option<BitSet<BSA>>, io::Error>
{
if !self.exists()
{
return Ok(None)
}
let mut bit_set = BitSet::<BSA>::new();
for entry in self.read_dir()?
{
let entry = entry?;
let file_type = entry.file_type()?;
if file_type.is_dir() || file_type.is_symlink()
{
if let Some(bsa) = BSA::parse_file_name(&entry.file_name())?
{
bit_set.add(bsa)
}
}
}
Ok(Some(bit_set))
}
#[inline(always)]
fn memory_map_read_write<'a>(&self, offset: u64, address_hint: AddressHint, sharing: Sharing, prefault: bool, reserve_swap_space: bool, page_size_or_huge_page_size_settings: &PageSizeOrHugePageSizeSettings) -> io::Result<MappedMemory>
{
const protection: Protection = Protection::ReadWrite;
let file = protection.adjust_open_options_to_match(&mut OpenOptions::new()).open(self)?;
let length =
{
let metadata = file.metadata()?;
if !metadata.is_file()
{
return Err(io_error_other("Not a file"))
}
metadata.len()
};
if length == 0
{
return Err(io_error_other("Empty files can not be memory-mapped"))
}
MappedMemory::from_file(&file, offset, new_non_zero_u64(length), address_hint, protection, sharing, prefault, reserve_swap_space, page_size_or_huge_page_size_settings).map_err(io_error_other)
}
}