bitvec/access.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
#![doc = include_str!("../doc/access.md")]
use core::sync::atomic::Ordering;
use funty::Integral;
use radium::Radium;
use crate::{
index::{
BitIdx,
BitMask,
},
mem::BitRegister,
order::BitOrder,
};
#[doc = include_str!("../doc/access/BitAccess.md")]
pub trait BitAccess: Radium
where <Self as Radium>::Item: BitRegister
{
/// Clears bits within a memory element to `0`.
///
/// The mask provided to this method must be constructed from indices that
/// are valid in the caller’s context. As the mask is already computed by
/// the caller, this does not take an ordering type parameter.
///
/// ## Parameters
///
/// - `mask`: A mask of any number of bits. This is a selection mask: all
/// bits in the mask that are set to `1` will set the corresponding bit in
/// `*self` to `0`.
///
/// ## Returns
///
/// The prior value of the memory element.
///
/// ## Effects
///
/// All bits in `*self` corresponding to `1` bits in the `mask` are cleared
/// to `0`; all others retain their original value.
///
/// Do not invert the `mask` prior to calling this function. [`BitMask`] is
/// a selection type, not a bitwise-operation argument.
///
/// [`BitMask`]: crate::index::BitMask
#[inline]
fn clear_bits(&self, mask: BitMask<Self::Item>) -> Self::Item {
self.fetch_and(!mask.into_inner(), Ordering::Relaxed)
}
/// Sets bits within a memory element to `1`.
///
/// The mask provided to this method must be constructed from indices that
/// are valid in the caller’s context. As the mask is already computed by
/// the caller, this does not take an ordering type parameter.
///
/// ## Parameters
///
/// - `mask`: A mask of any number of bits. This is a selection mask: all
/// bits in the mask that are set to `1` will set the corresponding bit in
/// `*self` to `1`.
///
/// ## Returns
///
/// The prior value of the memory element.
///
/// ## Effects
///
/// All bits in `*self` corresponding to `1` bits in the `mask` are set to
/// `1`; all others retain their original value.
#[inline]
fn set_bits(&self, mask: BitMask<Self::Item>) -> Self::Item {
self.fetch_or(mask.into_inner(), Ordering::Relaxed)
}
/// Inverts bits within a memory element.
///
/// The mask provided to this method must be constructed from indices that
/// are valid in the caller’s context. As the mask is already computed by
/// the caller, this does not take an ordering type parameter.
///
/// ## Parameters
///
/// - `mask`: A mask of any number of bits. This is a selection mask: all
/// bits in the mask that are set to `1` will invert the corresponding bit
/// in `*self`.
///
/// ## Returns
///
/// The prior value of the memory element.
///
/// ## Effects
///
/// All bits in `*self` corresponding to `1` bits in the `mask` are
/// inverted; all others retain their original value.
#[inline]
fn invert_bits(&self, mask: BitMask<Self::Item>) -> Self::Item {
self.fetch_xor(mask.into_inner(), Ordering::Relaxed)
}
/// Writes a value to one bit in a memory element, returning the previous
/// value.
///
/// ## Type Parameters
///
/// - `O`: An ordering of bits in a memory element that translates the
/// `index` into a real position.
///
/// ## Parameters
///
/// - `index`: The semantic index of the bit in `*self` to modify.
/// - `value`: The new bit value to write into `*self` at the `index`.
///
/// ## Returns
///
/// The bit previously stored in `*self` at `index`. These operations are
/// required to load the `*self` value from memory in order to operate, and
/// so always have the prior value available for use. This can reduce
/// spurious loads throughout the crate.
///
/// ## Effects
///
/// `*self` is updated with the bit at `index` set to `value`; all other
/// bits remain unchanged.
#[inline]
fn write_bit<O>(&self, index: BitIdx<Self::Item>, value: bool) -> bool
where O: BitOrder {
let select = index.select::<O>().into_inner();
select
& if value {
self.fetch_or(select, Ordering::Relaxed)
}
else {
self.fetch_and(!select, Ordering::Relaxed)
} != <Self::Item>::ZERO
}
/// Gets the function that will write `value` into all bits under a mask.
///
/// This is useful for preparing bulk operations that all write the same
/// data into memory, and only need to provide the shape of memory to write.
///
/// ## Parameters
///
/// - `value`: The bit that will be written by the returned function.
///
/// ## Returns
///
/// A function which writes `value` into memory at a given address and under
/// a given mask. If `value` is `false`, then this produces [`clear_bits`];
/// if it is `true`, then this produces [`set_bits`].
///
/// [`clear_bits`]: Self::clear_bits
/// [`set_bits`]: Self::set_bits
#[inline]
fn get_writers(
value: bool,
) -> for<'a> fn(&'a Self, BitMask<Self::Item>) -> Self::Item {
if value {
Self::set_bits
}
else {
Self::clear_bits
}
}
}
impl<A> BitAccess for A
where
A: Radium,
A::Item: BitRegister,
{
}
#[doc = include_str!("../doc/access/BitSafe.md")]
pub trait BitSafe {
/// The element type being guarded against improper mutation.
///
/// This is only present as an extra proof that the type graph has a
/// consistent view of the underlying memory.
type Mem: BitRegister;
/// The memory-access type this guards.
///
/// This is exposed as an associated type so that `BitStore` can name it
/// without having to re-select it based on crate configuration.
type Rad: Radium<Item = Self::Mem>;
/// The zero constant.
const ZERO: Self;
/// Loads the value from memory, allowing for the possibility that other
/// handles have write permissions to it.
fn load(&self) -> Self::Mem;
}
/// Constructs a shared-mutable guard type that disallows mutation *through it*.
macro_rules! safe {
($($t:ident => $w:ident => $r:ty);+ $(;)?) => { $(
#[derive(Debug)]
#[repr(transparent)]
#[doc = include_str!("../doc/access/impl_BitSafe.md")]
pub struct $w {
inner: <Self as BitSafe>::Rad,
}
impl $w {
/// Allow construction of the safed value by forwarding to its
/// interior constructor.
///
/// This type is not public API, and general use has no reason to
/// construct values of it directly. It is provided for convenience
/// as a crate internal.
pub(crate) const fn new(value: $t) -> Self {
Self { inner: <<Self as BitSafe>::Rad>::new(value) }
}
}
impl BitSafe for $w {
type Mem = $t;
#[cfg(feature = "atomic")]
type Rad = $r;
#[cfg(not(feature = "atomic"))]
type Rad = core::cell::Cell<$t>;
const ZERO: Self = Self::new(0);
#[inline]
fn load(&self) -> Self::Mem {
self.inner.load(Ordering::Relaxed)
}
}
)+ };
}
safe! {
u8 => BitSafeU8 => radium::types::RadiumU8;
u16 => BitSafeU16 => radium::types::RadiumU16;
u32 => BitSafeU32 => radium::types::RadiumU32;
}
#[cfg(target_pointer_width = "64")]
safe!(u64 => BitSafeU64 => radium::types::RadiumU64);
safe!(usize => BitSafeUsize => radium::types::RadiumUsize);
#[cfg(test)]
mod tests {
use core::cell::Cell;
use super::*;
use crate::prelude::*;
#[test]
fn touch_memory() {
let data = Cell::new(0u8);
let accessor = &data;
let aliased = unsafe { &*(&data as *const _ as *const BitSafeU8) };
assert!(!BitAccess::write_bit::<Lsb0>(
accessor,
BitIdx::new(1).unwrap(),
true
));
assert_eq!(aliased.load(), 2);
assert!(BitAccess::write_bit::<Lsb0>(
accessor,
BitIdx::new(1).unwrap(),
false
));
assert_eq!(aliased.load(), 0);
}
#[test]
#[cfg(not(miri))]
fn sanity_check_prefetch() {
use core::cell::Cell;
assert_eq!(
<Cell<u8> as BitAccess>::get_writers(false) as *const (),
<Cell<u8> as BitAccess>::clear_bits as *const ()
);
assert_eq!(
<Cell<u8> as BitAccess>::get_writers(true) as *const (),
<Cell<u8> as BitAccess>::set_bits as *const ()
);
}
}