lmao it actually works

master
Avril 4 years ago
parent 7741b4054a
commit 87c24ff87d
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -6,6 +6,13 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
# Ignore this, it is for regenerating the lookup table used on stable (needed due to lack of matching in `const fn`).
# It is already included in this repo.
# If you really want to rebuild it, `cargo test` will output it to `src/stable/mod.rs` lol.
speedup_hack_stable = []
[dependencies]
bitflags = "1.2"

@ -0,0 +1,32 @@
//! Benchmarks :^)
#![allow(unused_imports)]
use super::*;
extern crate test;
use test::{Bencher, black_box};
#[bench]
fn mask(b: &mut Bencher)
{
b.iter(|| {
for i in 0..0o777u32 {
let bits = Permissions::from(i);
black_box(bits);
}
});
}
#[bench]
fn from_mask(b: &mut Bencher)
{
b.iter(|| {
for i in 0..0o777u32 {
let bits = Permissions::from(i);
black_box(u32::from(bits));
}
});
}

@ -0,0 +1,31 @@
//! Ext for unixes
use super::*;
use std::os::unix::fs::PermissionsExt as UnixPermsExt;
use std::borrow::Borrow;
pub trait PermissionsExt
{
fn unix(&self) -> Permissions;
fn set_unix(&mut self, perm: impl Borrow<Permissions>);
fn from_unix(perm: impl Into<Permissions>) -> Self;
}
impl PermissionsExt for std::fs::Permissions
{
#[inline] fn unix(&self) -> Permissions
{
self.mode().into()
}
#[inline] fn set_unix(&mut self, perm: impl Borrow<Permissions>)
{
self.set_mode(perm.borrow().mask());
}
#[inline] fn from_unix(perm: impl Into<Permissions>) -> Self
{
Self::from_mode(perm.into().into())
}
}

@ -1,6 +1,7 @@
//! Unix permissions `mode_t` in readable format.
//!
//No I don't care about Microshaft wangblows
#![cfg_attr(nightly, feature(test))]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
@ -84,8 +85,7 @@ impl Class
}
}
#[cfg(nightly)]
const fn mask_mode(&self, bit: u32) -> Bit
#[cfg(nightly)] const fn mask_mode(&self, bit: u32) -> Bit
{
macro_rules! map {
($bit:expr, $bt:path, $constant:ident) => {
@ -111,9 +111,7 @@ impl Class
},
})
}
#[cfg(not(nightly))]
fn mask_mode(&self, bit: u32) -> Bit
#[cfg(not(nightly))] fn mask_mode(&self, bit: u32) -> Bit
{
macro_rules! map {
($bit:expr, $bt:path, $constant:ident) => {
@ -164,10 +162,19 @@ bitflags!{
/// # Notes
/// No this does not map directly to `mode_t`. Use `User.mode(bits)` and `User.from_mode(mode)`.
pub struct Bit: u32 {
/// No permissions for this class
const None = 0;
/// Read permissions for this class
const Read = 1;
/// Write permissions for this class
const Write = 2;
/// Execute permissions for this class
///
/// # Notes
/// For directories, this translates to `can chdir to here'
const Execute = 4;
/// Read + Write + Execute. The whole mask.
const Mask = 7;
}
}
@ -175,38 +182,64 @@ bitflags!{
bitflags!{
// not supported, don't care
struct StickyBit: u32{
const None = 0;
const Empty = 0;
const Setuid = 0o40000;
const Setgid = 0o20000;
const SaveSwap = 0o10000;
}
}
/// Permissions struct in readable format
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
#[repr(C)]
pub struct Permissions
{
/// The `S_IRWXU` mask.
///
/// # In English
/// Read, write, and execute mask for owner of the file
pub owner: Bit,
pub u_owner: Bit,
pub u_group: Bit,
pub u_other: Bit,
/// The `S_IRWXG` mask.
///
/// # In English
/// Read, write, and execute mask for members of the owner's group
pub group: Bit,
_u_sticky: StickyBit, //idc about this either
/// The `S_IRWXO` mask.
///
/// # In English
/// Read, write, and execute mask for anyone else
pub other: Bit,
_sticky: StickyBit, //idc about this either
}
/// The class of user that UNIX permissions care about
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub enum User
{
/// Owner of the file directly
Owner,
/// Members of the owner's group
Group,
/// Anyony else
Other,
}
impl User
{
/// Convert this class's representation of `Bit` into the corresponding `mode_t`.
pub const fn mode(self, mask: Bit) -> u32
#[cfg(nightly)] pub const fn mode(self, mask: Bit) -> u32
{
match self {
User::Owner => Class::Owner(mask).mode(),
User::Group => Class::Group(mask).mode(),
User::Other => Class::Other(mask).mode(),
}
}
/// Convert this class's representation of `Bit` into the corresponding `mode_t`.
#[cfg(not(nightly))] pub fn mode(self, mask: Bit) -> u32
{
match self {
User::Owner => Class::Owner(mask).mode(),
@ -214,9 +247,20 @@ impl User
User::Other => Class::Other(mask).mode(),
}
}
/// Convert a `mode_t` `u32` into `Bit` with this class' user.
pub const fn from_mode(self, mask: u32) -> Bit
#[cfg(nightly)] pub const fn from_mode(self, mask: u32) -> Bit
{
match self {
User::Owner => Class::Owner(Bit::Mask).mask_mode(mask),
User::Group => Class::Group(Bit::Mask).mask_mode(mask),
User::Other => Class::Other(Bit::Mask).mask_mode(mask),
}
}
/// Convert a `mode_t` `u32` into `Bit` with this class' user.
#[cfg(not(nightly))] pub fn from_mode(self, mask: u32) -> Bit
{
match self {
User::Owner => Class::Owner(Bit::Mask).mask_mode(mask),
@ -240,10 +284,10 @@ impl Permissions
pub const fn new() -> Self
{
Self {
u_owner: Bit::None,
u_group: Bit::None,
u_other: Bit::None,
_u_sticky: StickyBit::None,
owner: Bit::None,
group: Bit::None,
other: Bit::None,
_sticky: StickyBit::Empty,
}
}
}
@ -253,59 +297,101 @@ impl Permissions
{
#[inline] const fn c_owner(&self) -> Class
{
Class::Owner(self.u_owner)
Class::Owner(self.owner)
}
#[inline] const fn c_group(&self) -> Class
{
Class::Group(self.u_group)
Class::Group(self.group)
}
#[inline] const fn c_other(&self) -> Class
{
Class::Other(self.u_other)
Class::Other(self.other)
}
/// Convert into `mode_t` repr.
#[inline] pub const fn mask(&self) -> u32
/// Convert into `mode_t` representation.
#[inline] #[cfg(nightly)] pub const fn mask(&self) -> u32
{
self.c_owner().mode() |
self.c_group().mode() |
self.c_other().mode()
}
/// Convert from `mode_t` repr.
#[inline] pub const fn from_mask(bit: u32) -> Self
/// Convert into `mode_t` representation.
#[inline] #[cfg(not(nightly))] pub fn mask(&self) -> u32
{
self.c_owner().mode() |
self.c_group().mode() |
self.c_other().mode()
}
/// Convert from `mode_t` representation (slow).
#[inline] #[cfg(nightly)] const fn from_mask_calc(bit: u32) -> Self
{
Self{
u_owner: Class::Owner(Bit::Mask).mask_mode(bit),
u_group: Class::Group(Bit::Mask).mask_mode(bit),
u_other: Class::Other(Bit::Mask).mask_mode(bit),
owner: Class::Owner(Bit::Mask).mask_mode(bit),
group: Class::Group(Bit::Mask).mask_mode(bit),
other: Class::Other(Bit::Mask).mask_mode(bit),
_u_sticky: StickyBit::None,
_sticky: StickyBit::Empty,
}
}
/// Convert from `mode_t` representation.
#[inline] pub const fn from_mask(from: u32) -> Self
{
MAP[(from & MODE) as usize]
}
/// Consume and add a mask.
///
/// # Usage
/// For builder pattern
#[inline] pub const fn add_mask(self, class: User, bit: Bit) -> Self
#[inline] #[cfg(nightly)] pub const fn add_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
u_owner: Bit::from_bits_truncate(self.u_owner.bits() | bit.bits()),
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
..self
}
},
User::Group => {
Self {
u_group: Bit::from_bits_truncate(self.u_group.bits() | bit.bits()),
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self
}
},
User::Other => {
Self {
u_other: Bit::from_bits_truncate(self.u_other.bits() | bit.bits()),
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self
}
},
}
}
/// Consume and add a mask.
///
/// # Usage
/// For builder pattern
#[inline] #[cfg(not(nightly))] pub fn add_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self
}
},
@ -317,42 +403,87 @@ impl Permissions
///
/// # Usage
/// For builder pattern
#[inline] pub const fn remove_mask(self, class: User, bit: Bit) -> Self
#[inline] #[cfg(nightly)] pub const fn remove_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
u_owner: Bit::from_bits_truncate(self.u_owner.bits() & !bit.bits()),
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
..self
}
},
User::Group => {
Self {
u_group: Bit::from_bits_truncate(self.u_group.bits() & !bit.bits()),
group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
..self
}
},
User::Other => {
Self {
u_other: Bit::from_bits_truncate(self.u_other.bits() & !bit.bits()),
other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
..self
}
},
}
}
/// Consume and remove a mask.
///
/// # Usage
/// For builder pattern
#[inline] #[cfg(not(nightly))] pub fn remove_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
..self
}
},
}
}
/// Returns true if the specified mode mask is all present
#[inline] #[cfg(nightly)] pub const fn has_mask(&self, class: User, bit: Bit) -> bool
{
match class {
User::Owner => {
(self.owner.bits() & bit.bits()) == bit.bits()
},
User::Group => {
(self.group.bits() & bit.bits()) == bit.bits()
},
User::Other => {
(self.other.bits() & bit.bits()) == bit.bits()
},
}
}
/// Returns true if the specified mode mask is all present
#[inline] pub const fn has_mask(&self, class: User, bit: Bit) -> bool
#[inline] #[cfg(not(nightly))] pub fn has_mask(&self, class: User, bit: Bit) -> bool
{
match class {
User::Owner => {
(self.u_owner.bits() & bit.bits()) == bit.bits()
(self.owner.bits() & bit.bits()) == bit.bits()
},
User::Group => {
(self.u_group.bits() & bit.bits()) == bit.bits()
(self.group.bits() & bit.bits()) == bit.bits()
},
User::Other => {
(self.u_other.bits() & bit.bits()) == bit.bits()
(self.other.bits() & bit.bits()) == bit.bits()
},
}
}
@ -362,7 +493,7 @@ impl From<Permissions> for u32
{
#[inline] fn from(from: Permissions) -> Self
{
from.mask()
from.mask() //can we not do the `MAP` here ;~;
}
}
@ -375,3 +506,159 @@ impl From<u32> for Permissions
}
mod test;
#[cfg(nightly)] mod bench;
const fn generate_struct() -> [Permissions; 512]
{
#[cfg(nightly)] return {
let mut i: u32 =0;
let mut output = [Permissions::new(); 512];
loop
{
output[i as usize] = Permissions::from_mask_calc(i);
i+=1;
if i == 0o777 {break;}
}
output
};
#[cfg(not(nightly))] {
stable::MAP
}
}
#[cfg(feature="speedup_hack_stable")]
mod output {
use super::*;
use std::{
io::{
self,
Write,
},
};
pub fn print_map_as_syntax_hack<W: Write+?Sized>(to: &mut W) -> io::Result<()>
{
fn print_bit<W: Write+?Sized>(to:&mut W, bit: &Bit) -> io::Result<()>
{
macro_rules! dobit {
($bit:ident, $p:path) => {
if $bit.contains($p) {
write!(to, "| {}", $p.bits())?;
}
}
}
write!(to, "Bit::from_bits_truncate(0u32 ")?;
dobit!(bit, Bit::Read);
dobit!(bit, Bit::Write);
dobit!(bit, Bit::Execute);
write!(to, "| 0)")?;
Ok(())
}
writeln!(to, "[")?;
for perm in MAP.iter() {
write!(to, "Permissions {{owner: ")?;
print_bit(to,&perm.owner)?;
write!(to, ", group: ")?;
print_bit(to,&perm.group)?;
write!(to, ", other: ")?;
print_bit(to,&perm.other)?;
write!(to, ", _sticky: StickyBit::Empty}}, ")?;
}
writeln!(to, "]")?;
Ok(())
}
}
#[cfg(feature="speedup_hack_stable")]
fn print_map_as_syntax_hack()
{
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
output::print_map_as_syntax_hack(&mut stdout).unwrap();
}
/// Generates the build map for stable release.
#[cfg(feature="speedup_hack_stable")]
pub fn generate_build_map() {
use std::fs::OpenOptions;
use std::io::Write;
let mut file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open("./src/stable/mod.rs").expect("Failed to open file");
writeln!(&mut file, "//! Hack for fast in stable with no `const fn` stuffs\n").unwrap();
writeln!(&mut file, "use super::*;").unwrap();
writeln!(&mut file, "pub const MAP: [Permissions; 512] = ").unwrap();
output::print_map_as_syntax_hack(&mut file).expect("Failed to write");
writeln!(&mut file, ";").unwrap();
}
#[cfg(not(nightly))] mod stable;
const MAP: [Permissions; 512] = generate_struct();
#[cfg(target_family="unix")]
mod ext;
#[cfg(target_family="unix")]
pub use ext::*;
// Boilerplate
use std::{
borrow::Borrow,
cmp::{PartialEq,Eq},
};
impl AsRef<Permissions> for u32
{
fn as_ref(&self) -> &Permissions
{
&MAP[(*self & 0o777u32) as usize]
}
}
impl AsRef<Permissions> for Permissions
{
fn as_ref(&self) -> &Permissions
{
&self
}
}
impl Borrow<Permissions> for u32
{
fn borrow(&self) -> &Permissions
{
self.as_ref()
}
}
impl PartialEq<u32> for Permissions
{
fn eq(&self, other: &u32) -> bool
{
&Self::from(*other) == self
}
}
impl PartialEq<Permissions> for u32
{
fn eq(&self, other: &Permissions) -> bool
{
&Self::from(*self) == other
}
}
impl std::fmt::Display for Permissions
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
println!("0{}", self.mask())
}
}

File diff suppressed because one or more lines are too long

@ -22,3 +22,27 @@ fn from_mask()
assert!(mask.has_mask(User::Group, Bit::Read | Bit::Execute));
assert!(mask.has_mask(User::Other, Bit::Read));
}
#[test]
fn map()
{
assert_eq!(&MAP[0o666], &Permissions::from_mask(0o666));
#[cfg(feature="speedup_hack_stable")]
super::generate_build_map();
}
#[cfg(target_family="unix")]
#[test]
fn real_file()
{
use std::fs::OpenOptions;
let file = OpenOptions::new()
.read(true)
.open("Cargo.toml").expect("File not found");
let perms = file.metadata().expect("Couldn't stat").permissions().unix();
assert_eq!(perms, 0o644u32);
}

Loading…
Cancel
Save