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
/*! Proxy reference for `&mut bool`

`BitSlice` regions can easily produce *read* references to `bool`s that they
contain, by testing the bit and producing the appropriate `&'static bool`, but
Rust’s rules forbid production of a *write* reference to a `bool` stored within
a `BitSlice` region. This is because references `&mut T` must be dereferencable
addresses of type `T`, and `&mut BitSlice` is a non-dereferencable encoded
pointer.

As Rust does not permit defining a method on `&mut _` references that can be
used to store a value into the referenced location, this type must be used
instead. Rust’s strict use of references, rather than arbitrary referential
types, makes this the only major API incompatibility with the rest of the
standard library.
!*/

use crate::{
	access::BitAccess,
	index::BitIdx,
	order::BitOrder,
	slice::BitSlice,
	store::BitStore,
};

use core::{
	fmt::{
		self,
		Debug,
		Formatter,
	},
	marker::PhantomData,
	mem,
	ops::{
		Deref,
		DerefMut,
	},
	ptr::NonNull,
};

use wyz::fmt::FmtForward;

/** Proxy reference type, equivalent to `&mut bool`.

This is a two-word structure capable of correctly referring to a single bit in
a memory element. Because Rust does not permit reference-like objects in the
same manner that C++ does – `&T` and `&mut T` values are required to be
immediately-valid pointers, not objects – `bitvec` cannot manifest encoded
`&mut Bit` values in the same way that it can manifest `&mut BitSlice`.

Instead, this type implements `Deref` and `DerefMut` to an internal `bool` slot,
and in `Drop` commits the value of that `bool` to the proxied bit in the source
`BitSlice` from which the `BitMut` value was created. The combination of Rust’s
own exclusion rules and the aliasing type system in this library ensure that a
`BitMut` value has unique access to the bit it proxies, and the memory element
it uses will not have destructive data races from other views.

# Lifetimes

- `'a`: The lifetime of the source `&'a mut BitSlice` that created the `BitMut`.

# Type Parameters

- `O`: The `BitOrder` type parameter from the source `&mut BitSlice`.
- `T`: The `BitStore` type parameter from the source `&mut BitSlice`.

# Examples

```rust
use bitvec::prelude::*;

let bits = bits![mut 0; 2];

let (left, right) = bits.split_at_mut(1);
let mut first = left.get_mut(0).unwrap();
let second = right.get_mut(0).unwrap();

// Referential behavior
*first = true;
// Direct write
second.set(true);

drop(first); // it’s not a reference!
assert_eq!(bits, bits![1; 2]);
```
**/
pub struct BitMut<'a, O, T>
where
	O: BitOrder,
	T: 'a + BitStore,
{
	/// Accessing pointer to the containing element.
	addr: NonNull<T::Access>,
	/// Index of the proxied bit within the containing element.
	head: BitIdx<T::Mem>,
	/// A local cache for `Deref` usage.
	data: bool,
	/// This type is semantically equivalent to a mutable slice of length 1.
	_ref: PhantomData<&'a mut BitSlice<O, T>>,
}

impl<O, T> BitMut<'_, O, T>
where
	O: BitOrder,
	T: BitStore,
{
	/// Constructs a new proxy from provided element and bit addresses.
	///
	/// # Parameters
	///
	/// - `addr`: The address of a memory element, correctly typed for access.
	/// - `head`: The index of a bit within `*addr`.
	///
	/// # Safety
	///
	/// The caller must produce `addr`’s value from a valid reference, and its
	/// type from the correct access requirements at time of construction.
	#[inline]
	pub(crate) unsafe fn new_unchecked(
		addr: *const T::Access,
		head: BitIdx<T::Mem>,
	) -> Self
	{
		Self {
			_ref: PhantomData,
			addr: NonNull::new_unchecked(addr as *mut T::Access),
			head,
			data: (&*addr).get_bit::<O>(head),
		}
	}

	/// Writes a bit into the proxied location without an intermediate copy.
	///
	/// This function writes `value` directly into the proxied location, and
	/// does not store `value` in the proxy’s internal cache. This should be
	/// equivalent to the behavior seen when using ordinary `DerefMut` proxying,
	/// but the latter depends on compiler optimization.
	///
	/// # Parameters
	///
	/// - `self`: This destroys the proxy, as it becomes invalid when writing
	///   directly to the location without updating the cache.
	/// - `value`: The new bit to write into the proxied slot.
	#[inline]
	pub fn set(mut self, value: bool) {
		self.write(value);
		mem::forget(self);
	}

	/// Commits a bit into memory.
	///
	/// This is the internal function used to drive `.set()` and `.drop()`.
	#[inline]
	fn write(&mut self, value: bool) {
		unsafe { (&*self.addr.as_ptr()).write_bit::<O>(self.head, value) }
	}
}

#[cfg(not(tarpaulin_include))]
impl<O, T> Debug for BitMut<'_, O, T>
where
	O: BitOrder,
	T: BitStore,
{
	#[inline]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		write!(fmt, "BitMut<{}>", core::any::type_name::<T::Mem>())?;
		fmt.debug_struct("")
			.field("addr", &self.addr.as_ptr().fmt_pointer())
			.field("head", &self.head.fmt_binary())
			.field("data", &self.data)
			.finish()
	}
}

impl<O, T> Deref for BitMut<'_, O, T>
where
	O: BitOrder,
	T: BitStore,
{
	type Target = bool;

	#[inline]
	fn deref(&self) -> &Self::Target {
		&self.data
	}
}

impl<O, T> DerefMut for BitMut<'_, O, T>
where
	O: BitOrder,
	T: BitStore,
{
	#[inline]
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.data
	}
}

impl<O, T> Drop for BitMut<'_, O, T>
where
	O: BitOrder,
	T: BitStore,
{
	#[inline(always)]
	fn drop(&mut self) {
		let value = self.data;
		self.write(value);
	}
}

#[cfg(test)]
mod tests {
	use crate::prelude::*;

	#[test]
	fn proxy_ref() {
		let mut data = 0u32;
		let bits = BitSlice::<Lsb0, _>::from_element_mut(&mut data);
		assert!(!bits[0]);

		let mut proxy = bits.first_mut().unwrap();
		*proxy = true;

		//  We can inspect the cache, but `proxy` locks the entire `bits` for
		//  the duration of its binding, so we cannot observe that the cache is
		//  not written into the main buffer.
		assert!(*proxy);
		drop(proxy);

		//  The proxy commits the cache on drop, releasing its lock on the main
		//  buffer, permitting us to see that the writeback occurred.
		assert!(bits[0]);
		assert_eq!(data, 1);
	}
}