use core::fmt;
use core::ops::BitOr;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Flags(u16);
impl Flags {
pub const NONE: Flags = Flags(0);
pub const CF: Flags = Flags(1 << 0);
pub const PF: Flags = Flags(1 << 2);
pub const AF: Flags = Flags(1 << 4);
pub const ZF: Flags = Flags(1 << 6);
pub const SF: Flags = Flags(1 << 7);
pub const OF: Flags = Flags(1 << 11);
pub const ALL: Flags = Flags(1 | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 7) | (1 << 11));
#[inline]
#[must_use]
pub const fn bits(self) -> u16 {
self.0
}
#[inline]
#[must_use]
pub const fn union(self, other: Flags) -> Flags {
Flags(self.0 | other.0)
}
pub(crate) const fn without(self, other: Flags) -> Flags {
Flags(self.0 & !other.0)
}
#[inline]
#[must_use]
pub const fn contains(self, other: Flags) -> bool {
self.0 & other.0 == other.0
}
#[inline]
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl BitOr for Flags {
type Output = Flags;
#[inline]
fn bitor(self, rhs: Flags) -> Flags {
self.union(rhs)
}
}
const FLAG_NAMES: [(Flags, &str); 6] = [
(Flags::CF, "CF"),
(Flags::PF, "PF"),
(Flags::AF, "AF"),
(Flags::ZF, "ZF"),
(Flags::SF, "SF"),
(Flags::OF, "OF"),
];
impl fmt::Display for Flags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return f.write_str("none");
}
let mut first = true;
for (flag, name) in FLAG_NAMES {
if self.contains(flag) {
if !first {
f.write_str("|")?;
}
f.write_str(name)?;
first = false;
}
}
Ok(())
}
}
impl fmt::Debug for Flags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct FlagEffects {
read: Flags,
written: Flags,
undefined: Flags,
}
impl FlagEffects {
pub(crate) const fn new(read: Flags, written: Flags, undefined: Flags) -> FlagEffects {
FlagEffects {
read,
written,
undefined,
}
}
pub(crate) const fn with_read(self, read: Flags) -> FlagEffects {
FlagEffects {
read: self.read.union(read),
..self
}
}
#[inline]
#[must_use]
pub const fn read(&self) -> Flags {
self.read
}
#[inline]
#[must_use]
pub const fn written(&self) -> Flags {
self.written
}
#[inline]
#[must_use]
pub const fn undefined(&self) -> Flags {
self.undefined
}
#[inline]
#[must_use]
pub const fn clobbered(&self) -> Flags {
self.written.union(self.undefined)
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
#[test]
fn test_flags_set_operations() {
let f = Flags::CF | Flags::OF;
assert!(f.contains(Flags::CF) && f.contains(Flags::OF) && !f.contains(Flags::ZF));
assert!(Flags::ALL.contains(f));
assert!(!f.is_empty() && Flags::NONE.is_empty() && Flags::default().is_empty());
assert_eq!(Flags::ALL.bits(), 0x8D5);
}
#[test]
fn test_flags_display_order() {
assert_eq!(Flags::ALL.to_string(), "CF|PF|AF|ZF|SF|OF");
assert_eq!((Flags::OF | Flags::CF).to_string(), "CF|OF");
assert_eq!(alloc::format!("{:?}", Flags::NONE), "none");
}
#[test]
fn test_flag_effects_clobbered() {
let e = FlagEffects::new(Flags::CF, Flags::ZF, Flags::AF).with_read(Flags::OF);
assert_eq!(e.read(), Flags::CF | Flags::OF);
assert_eq!(e.clobbered(), Flags::ZF | Flags::AF);
}
}