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}