mirror of
https://github.com/obhq/obliteration.git
synced 2025-04-02 11:02:08 -04:00
Switches fork1 flags from bitfield to bitflag (#1290)
This commit is contained in:
parent
ea6b1513a5
commit
39ceb714f0
12 changed files with 249 additions and 84 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -432,6 +432,10 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflag"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -2925,6 +2929,7 @@ name = "obkrnl"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitfield-struct",
|
||||
"bitflag",
|
||||
"config",
|
||||
"hashbrown",
|
||||
"krt",
|
||||
|
|
|
@ -6,6 +6,7 @@ members = [
|
|||
"config",
|
||||
"gui",
|
||||
"kernel",
|
||||
"lib/bitflag",
|
||||
"lib/krt",
|
||||
"macros",
|
||||
]
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
bitfield-struct = "0.9.2"
|
||||
bitflag = { path = "../lib/bitflag" }
|
||||
config = { path = "../config" }
|
||||
hashbrown = "0.14.5"
|
||||
krt = { path = "../lib/krt" }
|
||||
|
|
|
@ -91,11 +91,16 @@ fn init_vm(procs: &Arc<ProcMgr>) -> Arc<Uma> {
|
|||
Uma::new(vm)
|
||||
}
|
||||
|
||||
/// See `create_init` function on the PS4 for a reference.
|
||||
/// See `create_init` function on the Orbis for a reference.
|
||||
///
|
||||
/// # Reference offsets
|
||||
/// | Version | Offset |
|
||||
/// |---------|--------|
|
||||
/// |PS4 11.00|0x2BEF30|
|
||||
fn create_init() {
|
||||
let pmgr = current_procmgr().unwrap();
|
||||
let abi = Arc::new(Ps4Abi);
|
||||
let flags = Fork::new().with_copy_fd(true).with_create_process(true);
|
||||
let flags = Fork::CopyFd | Fork::CreateProcess;
|
||||
|
||||
pmgr.fork(abi, flags).unwrap();
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ use crate::lock::{MappedMutex, Mutex, MutexGuard};
|
|||
use crate::signal::Signal;
|
||||
use crate::subsystem::Subsystem;
|
||||
use alloc::sync::{Arc, Weak};
|
||||
use bitfield_struct::bitfield;
|
||||
use core::error::Error;
|
||||
use core::fmt::{Display, Formatter};
|
||||
use hashbrown::HashMap;
|
||||
use macros::bitflag;
|
||||
|
||||
mod abi;
|
||||
mod cell;
|
||||
|
@ -48,19 +48,24 @@ impl ProcMgr {
|
|||
MutexGuard::map(self.procs.lock(), |procs| procs.values())
|
||||
}
|
||||
|
||||
/// We imply `RFSTOPPED` to make [`ProcMgr`] not depend on a scheduler.
|
||||
/// We imply `RFSTOPPED` to make [`ProcMgr`] not depend on the scheduler.
|
||||
///
|
||||
/// See `fork1` on the PS4 for a reference.
|
||||
/// See `fork1` on the Orbis for a reference.
|
||||
///
|
||||
/// # Reference offsets
|
||||
/// | Version | Offset |
|
||||
/// |---------|--------|
|
||||
/// |PS4 11.00|0x14B830|
|
||||
pub fn fork(&self, abi: Arc<dyn ProcAbi>, flags: Fork) -> Result<Arc<Proc>, ForkError> {
|
||||
// TODO: Refactor this for readability.
|
||||
if (flags.into_bits() & 0x60008f8b) != 0
|
||||
|| flags.copy_fd() && flags.clear_fd()
|
||||
|| flags.parent_signal().into_bits() != 0 && !flags.custom_signal()
|
||||
if (u32::from(flags) & 0x60008f8b) != 0
|
||||
|| flags.has_all(Fork::CopyFd | Fork::ClearFd)
|
||||
|| flags.has_any(Fork::ParentSignal.mask()) && !flags.has_any(Fork::CustomSignal)
|
||||
{
|
||||
return Err(ForkError::InvalidFlags);
|
||||
}
|
||||
|
||||
if !flags.create_process() {
|
||||
if !flags.has_any(Fork::CreateProcess) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -89,52 +94,31 @@ pub struct ProcEvents {
|
|||
}
|
||||
|
||||
/// Flags to control behavior of [`ProcMgr::fork()`].
|
||||
#[bitfield(u32)]
|
||||
pub struct Fork {
|
||||
__: bool,
|
||||
__: bool,
|
||||
#[bitflag(u32)]
|
||||
pub enum Fork {
|
||||
/// Duplicate file descriptor table to the child instead of sharing it with the parent. Cannot
|
||||
/// used together with [`Self::clear_fd()`].
|
||||
///
|
||||
/// This has the same value as `RFFDG`.
|
||||
pub copy_fd: bool,
|
||||
__: bool,
|
||||
CopyFd = 0x4,
|
||||
/// Create a child process.
|
||||
///
|
||||
/// This has the same value as `RFPROC`.
|
||||
pub create_process: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
CreateProcess = 0x10,
|
||||
/// Create an empty file descriptor table for the child. Cannot used together with
|
||||
/// [`Self::copy_fd()`].
|
||||
///
|
||||
/// This has the same value as `RFCFDG`.
|
||||
pub clear_fd: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
ClearFd = 0x1000,
|
||||
/// Enable [`Self::parent_signal()`].
|
||||
///
|
||||
/// This has the same value as `RFTSIGZMB`.
|
||||
pub custom_signal: bool,
|
||||
CustomSignal = 0x80000,
|
||||
/// Use this signal instead of `SIGCHLD` to notify the parent. Requires
|
||||
/// [`Self::custom_signal()`] to be enabled.
|
||||
///
|
||||
/// This has the same value produced by `RFTSIGNUM` macro.
|
||||
#[bits(8)]
|
||||
pub parent_signal: Signal,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
ParentSignal(Signal) = 0xFF00000,
|
||||
}
|
||||
|
||||
/// Represents an error when [`ProcMgr::fork()`] fails.
|
||||
|
|
|
@ -37,21 +37,21 @@ impl UmaKeg {
|
|||
align: usize,
|
||||
mut flags: UmaFlags,
|
||||
) -> Self {
|
||||
if flags.has(UmaFlags::Vm) {
|
||||
if flags.has_any(UmaFlags::Vm) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
if flags.has(UmaFlags::ZInit) {
|
||||
if flags.has_any(UmaFlags::ZInit) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
if flags.has(UmaFlags::Malloc | UmaFlags::RefCnt) {
|
||||
if flags.has_any(UmaFlags::Malloc | UmaFlags::RefCnt) {
|
||||
flags |= UmaFlags::VToSlab;
|
||||
}
|
||||
|
||||
// Get header layout.
|
||||
let hdr = Layout::new::<Slab<()>>();
|
||||
let (mut hdr, off) = if flags.has(UmaFlags::RefCnt) {
|
||||
let (mut hdr, off) = if flags.has_any(UmaFlags::RefCnt) {
|
||||
hdr.extend(Layout::new::<RcFree>()).unwrap()
|
||||
} else {
|
||||
hdr.extend(Layout::new::<Free>()).unwrap()
|
||||
|
@ -64,7 +64,7 @@ impl UmaKeg {
|
|||
let available = PAGE_SIZE.get() - hdr.size();
|
||||
|
||||
// Get uk_ppera and uk_ipers.
|
||||
let (ppera, ipers) = if flags.has(UmaFlags::CacheSpread) {
|
||||
let (ppera, ipers) = if flags.has_any(UmaFlags::CacheSpread) {
|
||||
// Round size.
|
||||
let rsize = if (size.get() & align) == 0 {
|
||||
size.get()
|
||||
|
@ -93,10 +93,10 @@ impl UmaKeg {
|
|||
// TODO: Not sure why we need space at least for 2 free item?
|
||||
if (size.get() + free_item) > available {
|
||||
// TODO: Set uk_ppera and uk_rsize.
|
||||
if !flags.has(UmaFlags::Internal) {
|
||||
if !flags.has_any(UmaFlags::Internal) {
|
||||
flags |= UmaFlags::Offpage;
|
||||
|
||||
if !flags.has(UmaFlags::VToSlab) {
|
||||
if !flags.has_any(UmaFlags::VToSlab) {
|
||||
flags |= UmaFlags::Hash;
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ impl UmaKeg {
|
|||
let ipers = available / (rsize + free_item);
|
||||
|
||||
// TODO: Verify if this valid for PAGE_SIZE < 0x4000.
|
||||
if !flags.has(UmaFlags::Internal | UmaFlags::CacheOnly)
|
||||
if !flags.has_any(UmaFlags::Internal | UmaFlags::CacheOnly)
|
||||
&& (available % (rsize + free_item)) >= Uma::MAX_WASTE.get()
|
||||
&& (PAGE_SIZE.get() / rsize) > ipers
|
||||
{
|
||||
|
@ -134,8 +134,8 @@ impl UmaKeg {
|
|||
}
|
||||
};
|
||||
|
||||
if flags.has(UmaFlags::Offpage) {
|
||||
if flags.has(UmaFlags::RefCnt) {
|
||||
if flags.has_any(UmaFlags::Offpage) {
|
||||
if flags.has_any(UmaFlags::RefCnt) {
|
||||
// TODO: Set uk_slabzone to slabrefzone.
|
||||
} else {
|
||||
// TODO: Set uk_slabzone to slabzone.
|
||||
|
@ -150,11 +150,11 @@ impl UmaKeg {
|
|||
Self::page_alloc
|
||||
};
|
||||
|
||||
if flags.has(UmaFlags::MtxClass) {
|
||||
if flags.has_any(UmaFlags::MtxClass) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
if !flags.has(UmaFlags::Offpage) {
|
||||
if !flags.has_any(UmaFlags::Offpage) {
|
||||
let space = ppera * PAGE_SIZE.get();
|
||||
let pgoff = (space - hdr.size()) - ipers * free_item;
|
||||
|
||||
|
@ -164,7 +164,7 @@ impl UmaKeg {
|
|||
}
|
||||
}
|
||||
|
||||
if flags.has(UmaFlags::Hash) {
|
||||
if flags.has_any(UmaFlags::Hash) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ impl UmaKeg {
|
|||
/// |PS4 11.00|0x141E20|
|
||||
pub fn fetch_slab(&mut self, _: &UmaZone, flags: Alloc) -> Option<()> {
|
||||
while self.free == 0 {
|
||||
if flags.has(Alloc::NoVm) {
|
||||
if flags.has_any(Alloc::NoVm) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -233,10 +233,10 @@ impl UmaKeg {
|
|||
/// |---------|--------|
|
||||
/// |PS4 11.00|0x13FBA0|
|
||||
fn alloc_slab(&self, flags: Alloc) {
|
||||
if self.flags.has(UmaFlags::Offpage) {
|
||||
if self.flags.has_any(UmaFlags::Offpage) {
|
||||
todo!()
|
||||
} else {
|
||||
let flags = if self.flags.has(UmaFlags::Malloc) {
|
||||
let flags = if self.flags.has_any(UmaFlags::Malloc) {
|
||||
flags & !Alloc::Zero
|
||||
} else {
|
||||
flags | Alloc::Zero
|
||||
|
|
|
@ -100,7 +100,7 @@ impl Uma {
|
|||
name: impl Into<String>,
|
||||
size: NonZero<usize>,
|
||||
align: Option<usize>,
|
||||
flags: UmaFlags,
|
||||
flags: impl Into<UmaFlags>,
|
||||
) -> UmaZone {
|
||||
// The Orbis will allocate a new zone from masterzone_z. We choose to remove this since it
|
||||
// does not idomatic to Rust, which mean our uma_zone itself can live on the stack.
|
||||
|
|
|
@ -53,10 +53,11 @@ impl UmaZone {
|
|||
keg: Option<UmaKeg>,
|
||||
size: NonZero<usize>,
|
||||
align: Option<usize>,
|
||||
flags: UmaFlags,
|
||||
flags: impl Into<UmaFlags>,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
let (keg, mut flags) = if flags.has(UmaFlags::Secondary) {
|
||||
let flags = flags.into();
|
||||
let (keg, mut flags) = if flags.has_any(UmaFlags::Secondary) {
|
||||
todo!()
|
||||
} else {
|
||||
// We use a different approach here to make it idiomatic to Rust. On Orbis it will
|
||||
|
@ -74,8 +75,8 @@ impl UmaZone {
|
|||
let mut ty = ZoneType::Other;
|
||||
let mut count = 0;
|
||||
|
||||
if !keg.flags().has(UmaFlags::Internal) {
|
||||
count = if !keg.flags().has(UmaFlags::MaxBucket) {
|
||||
if !keg.flags().has_any(UmaFlags::Internal) {
|
||||
count = if !keg.flags().has_any(UmaFlags::MaxBucket) {
|
||||
min(keg.item_per_slab(), Uma::BUCKET_MAX)
|
||||
} else {
|
||||
Uma::BUCKET_MAX
|
||||
|
@ -148,7 +149,7 @@ impl UmaZone {
|
|||
/// |---------|--------|
|
||||
/// |PS4 11.00|0x13E750|
|
||||
pub fn alloc(&self, flags: Alloc) -> *mut u8 {
|
||||
if flags.has(Alloc::Wait) {
|
||||
if flags.has_any(Alloc::Wait) {
|
||||
// TODO: The Orbis also modify td_pflags on a certain condition.
|
||||
let td = current_thread();
|
||||
|
||||
|
@ -210,7 +211,7 @@ impl UmaZone {
|
|||
| ZoneType::Mbuf
|
||||
| ZoneType::MbufCluster
|
||||
) {
|
||||
if flags.has(Alloc::Wait) {
|
||||
if flags.has_any(Alloc::Wait) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -272,7 +273,7 @@ impl UmaZone {
|
|||
// Get allocation flags.
|
||||
let mut flags = flags & !Alloc::Zero;
|
||||
|
||||
if self.flags.has(UmaFlags::CacheOnly) {
|
||||
if self.flags.has_any(UmaFlags::CacheOnly) {
|
||||
flags |= Alloc::NoVm;
|
||||
}
|
||||
|
||||
|
@ -317,13 +318,13 @@ impl UmaZone {
|
|||
let mut kegs = self.kegs.write();
|
||||
let keg = keg.unwrap_or(kegs.front_mut().unwrap());
|
||||
|
||||
if !keg.flags().has(UmaFlags::Bucket) || keg.recurse() == 0 {
|
||||
if !keg.flags().has_any(UmaFlags::Bucket) || keg.recurse() == 0 {
|
||||
loop {
|
||||
if let Some(v) = keg.fetch_slab(self, flags) {
|
||||
return Some(v);
|
||||
}
|
||||
|
||||
if flags.has(Alloc::NoWait | Alloc::NoVm) {
|
||||
if flags.has_any(Alloc::NoWait | Alloc::NoVm) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
6
lib/bitflag/Cargo.toml
Normal file
6
lib/bitflag/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "bitflag"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
17
lib/bitflag/src/lib.rs
Normal file
17
lib/bitflag/src/lib.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
#![no_std]
|
||||
|
||||
pub use self::mask::*;
|
||||
|
||||
use core::ops::{BitOr, Not};
|
||||
|
||||
mod mask;
|
||||
|
||||
/// Type of bit flag.
|
||||
pub trait Type: From<Self::Raw> {
|
||||
type Raw: Raw;
|
||||
}
|
||||
|
||||
/// Underlying type of [`Type`].
|
||||
pub trait Raw: BitOr<Output = Self> + Not<Output = Self> + Copy {}
|
||||
|
||||
impl Raw for u32 {}
|
49
lib/bitflag/src/mask.rs
Normal file
49
lib/bitflag/src/mask.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use crate::Type;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{BitOr, Not};
|
||||
|
||||
/// Mask of a value in the set.
|
||||
pub struct Mask<T: Type, V> {
|
||||
mask: T::Raw,
|
||||
phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<T: Type, V> Mask<T, V> {
|
||||
/// # Safety
|
||||
/// `mask` must be valid for `V` (e.g. has exactly one bit set if `V` is `bool`) and must not
|
||||
/// have zero bit in the middle.
|
||||
pub const unsafe fn new(mask: T::Raw) -> Self {
|
||||
Self {
|
||||
mask,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn mask(self) -> T::Raw {
|
||||
self.mask
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, V> Clone for Mask<T, V> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, V> Copy for Mask<T, V> {}
|
||||
|
||||
impl<T: Type> BitOr for Mask<T, bool> {
|
||||
type Output = T;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
T::from(self.mask | rhs.mask)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, V> Not for Mask<T, V> {
|
||||
type Output = T;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
T::from(!self.mask)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use quote::{ToTokens, quote};
|
||||
use std::num::NonZero;
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{Error, Fields, ItemEnum, Path};
|
||||
use syn::punctuated::Pair;
|
||||
use syn::{Error, Expr, ExprLit, Fields, ItemEnum, Lit, LitInt, Path, Type, parse_quote};
|
||||
|
||||
pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
||||
let ty = opts
|
||||
|
@ -18,10 +20,22 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
let mut body = TokenStream::new();
|
||||
|
||||
for v in item.variants {
|
||||
let attrs = v.attrs;
|
||||
// Parse discriminant.
|
||||
let ident = v.ident;
|
||||
let discriminant = match v.discriminant {
|
||||
Some(v) => v.1,
|
||||
let (mask, bits, dis) = match v.discriminant {
|
||||
Some((
|
||||
_,
|
||||
Expr::Lit(ExprLit {
|
||||
attrs: _,
|
||||
lit: Lit::Int(v),
|
||||
}),
|
||||
)) => parse_discriminant(ty, v)?,
|
||||
Some((_, v)) => {
|
||||
return Err(Error::new_spanned(
|
||||
v,
|
||||
"discriminant other than integer literal is not supported",
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new_spanned(
|
||||
ident,
|
||||
|
@ -30,14 +44,51 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
}
|
||||
};
|
||||
|
||||
if !matches!(v.fields, Fields::Unit) {
|
||||
return Err(Error::new_spanned(ident, "only unit variant is supported"));
|
||||
// Generate flag.
|
||||
let attrs = v.attrs;
|
||||
let ty = match v.fields {
|
||||
Fields::Named(_) => {
|
||||
return Err(Error::new_spanned(
|
||||
ident,
|
||||
"variant with named fields is not supported",
|
||||
));
|
||||
}
|
||||
Fields::Unnamed(mut v) => {
|
||||
// Get field.
|
||||
let f = match v.unnamed.pop() {
|
||||
Some(Pair::End(v)) => v,
|
||||
Some(_) => {
|
||||
return Err(Error::new_spanned(
|
||||
ident,
|
||||
"variant with multiple fields is not supported",
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new_spanned(
|
||||
ident,
|
||||
"field-less variant is not supported",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
f.ty
|
||||
}
|
||||
Fields::Unit => parse_quote!(bool),
|
||||
};
|
||||
|
||||
if let Type::Path(t) = &ty {
|
||||
if t.qself.is_none() && t.path.is_ident("bool") && bits.get() != 1 {
|
||||
return Err(Error::new_spanned(
|
||||
dis,
|
||||
"multiple bits for a boolean mask is not supported",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
body.extend(quote! {
|
||||
#(#attrs)*
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const #ident: Self = Self(#discriminant);
|
||||
pub const #ident: ::bitflag::Mask<Self, #ty> = unsafe { ::bitflag::Mask::new(#mask) };
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -52,8 +103,8 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
///
|
||||
/// This performs the `&` operation on the underlying value and check if the results is
|
||||
/// non-zero.
|
||||
pub const fn has(self, rhs: Self) -> bool {
|
||||
(self.0 & rhs.0) != 0
|
||||
pub fn has_any(self, rhs: impl Into<Self>) -> bool {
|
||||
(self.0 & rhs.into().0) != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if this set contains **all** flags in the `rhs` set.
|
||||
|
@ -82,17 +133,27 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
#body
|
||||
}
|
||||
|
||||
impl ::bitflag::Type for #impl_ident {
|
||||
type Raw = #ty;
|
||||
}
|
||||
|
||||
impl From<#ty> for #impl_ident {
|
||||
fn from(value: #ty) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::BitOr for #impl_ident {
|
||||
impl From<::bitflag::Mask<Self, bool>> for #impl_ident {
|
||||
fn from(value: ::bitflag::Mask<Self, bool>) -> Self {
|
||||
Self(value.mask())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::BitOr<::bitflag::Mask<Self, bool>> for #impl_ident {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
fn bitor(self, rhs: ::bitflag::Mask<Self, bool>) -> Self::Output {
|
||||
Self(self.0 | rhs.mask())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +163,12 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::BitOrAssign<::bitflag::Mask<Self, bool>> for #impl_ident {
|
||||
fn bitor_assign(&mut self, rhs: ::bitflag::Mask<Self, bool>) {
|
||||
self.0 |= rhs.mask();
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::BitAnd for #impl_ident {
|
||||
type Output = Self;
|
||||
|
||||
|
@ -110,14 +177,6 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::Not for #impl_ident {
|
||||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
Self(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#impl_ident> for #ty {
|
||||
fn from(value: #impl_ident) -> Self {
|
||||
value.0
|
||||
|
@ -126,6 +185,43 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
|
|||
})
|
||||
}
|
||||
|
||||
fn parse_discriminant(ty: &Path, dis: LitInt) -> syn::Result<(TokenStream, NonZero<u32>, LitInt)> {
|
||||
let v = if ty.is_ident("u32") {
|
||||
let v = dis.base10_parse::<u32>()?;
|
||||
let i = v.trailing_zeros();
|
||||
let mut r = v >> i;
|
||||
|
||||
// Disallow zero value.
|
||||
if r == 0 {
|
||||
return Err(Error::new_spanned(
|
||||
dis,
|
||||
"zero discriminant is not supported",
|
||||
));
|
||||
}
|
||||
|
||||
// Disallow zero bit in the middle.
|
||||
let mut n = 0;
|
||||
|
||||
while r != 0 {
|
||||
if r & 1 == 0 {
|
||||
return Err(Error::new_spanned(
|
||||
dis,
|
||||
"discriminant with non-contiguous bits is not supported",
|
||||
));
|
||||
}
|
||||
|
||||
n += 1;
|
||||
r >>= 1;
|
||||
}
|
||||
|
||||
(v.into_token_stream(), n.try_into().unwrap(), dis)
|
||||
} else {
|
||||
return Err(Error::new_spanned(ty, "unsupported underlying type"));
|
||||
};
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Options {
|
||||
ty: Option<Path>,
|
||||
|
|
Loading…
Add table
Reference in a new issue