bitvec/
access.rs

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