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 ()
		);
	}
}