dataview/
data_view.rs

1use core::{mem, ops, ptr, slice};
2use super::*;
3
4/// Read and write data to and from the underlying byte buffer.
5///
6/// # Operations
7///
8/// Each set of operations may support a try, panicking and unchecked variations, see below for more information.
9///
10/// * `read(offset)`
11///
12///   Reads a (potentially unaligned) value out of the view.
13///
14/// * `read_into(offset, dest)`
15///
16///   Reads a (potentially unaligned) value out of the view into the dest argument.
17///
18/// * `get(offset)`
19///
20///   Gets a reference to the data given the offset.
21///   Errors if the final pointer is misaligned for the given type.
22///
23/// * `get_mut(offset)`
24///
25///   Gets a mutable reference to the data given the offset.
26///   Errors if the final pointer is misaligned for the given type.
27///
28/// * `slice(offset, len)`
29///
30///   Gets a slice to the data given the offset and len.
31///   Errors if the final pointer is misaligned for the given type.
32///
33/// * `slice_mut(offset, len)`
34///
35///   Gets a mutable slice to the data given the offset.
36///   Errors if the final pointer is misaligned for the given type.
37///
38/// * `write(offset, value)`
39///
40///   Writes a value to the view at the given offset.
41///
42/// # Panics
43///
44/// *Panicking* methods have no prefix or suffix. They invoke the *Try* methods and panic if they return `None`.
45///
46/// When calling *Panicking* variation with an offset that ends up out of bounds or if the final pointer is misaligned
47/// for the given type the method panics with the message `"invalid offset"`.
48///
49/// The relevant methods are annotated with `#[track_caller]` providing a useful location where the error happened.
50///
51/// # Safety
52///
53/// The *Unchecked* methods have the `_unchecked` suffix and simply assume the offset is correct.
54/// This is *Undefined Behavior* when it results in an out of bounds read or write or if a misaligned reference is produced.
55///
56/// If the *Try* variation returns `None` then the *Unchecked* variation invokes *Undefined Behavior*.
57#[repr(transparent)]
58pub struct DataView {
59	bytes: [u8],
60}
61
62impl DataView {
63	/// Returns a data view into the object's memory.
64	#[inline]
65	pub fn from<T: ?Sized + Pod>(v: &T) -> &DataView {
66		unsafe { mem::transmute(bytes(v)) }
67	}
68	/// Returns a mutable data view into the object's memory.
69	#[inline]
70	pub fn from_mut<T: ?Sized + Pod>(v: &mut T) -> &mut DataView {
71		unsafe { mem::transmute(bytes_mut(v)) }
72	}
73}
74
75unsafe impl Pod for DataView {}
76
77impl AsRef<[u8]> for DataView {
78	#[inline]
79	fn as_ref(&self) -> &[u8] {
80		&self.bytes
81	}
82}
83impl AsMut<[u8]> for DataView {
84	#[inline]
85	fn as_mut(&mut self) -> &mut [u8] {
86		&mut self.bytes
87	}
88}
89
90impl DataView {
91	/// Returns the number of bytes in the instance.
92	#[inline]
93	pub const fn len(&self) -> usize {
94		self.bytes.len()
95	}
96	/// Returns the number of elements that would fit a slice starting at the given offset.
97	#[inline]
98	pub const fn tail_len<T>(&self, offset: usize) -> usize {
99		(self.bytes.len() - offset) / mem::size_of::<T>()
100	}
101}
102
103//----------------------------------------------------------------
104
105/// Reads a (potentially unaligned) value from the view.
106impl DataView {
107	/// Reads a (potentially unaligned) value from the view.
108	#[inline]
109	pub fn try_read<T: Pod>(&self, offset: usize) -> Option<T> {
110		let index = offset..offset + mem::size_of::<T>();
111		let bytes = self.bytes.get(index)?;
112		unsafe {
113			let src = bytes.as_ptr() as *const T;
114			Some(ptr::read_unaligned(src))
115		}
116	}
117	/// Reads a (potentially unaligned) value from the view.
118	#[track_caller]
119	#[inline]
120	pub fn read<T: Pod>(&self, offset: usize) -> T {
121		match self.try_read(offset) {
122			Some(value) => value,
123			None => invalid_offset(),
124		}
125	}
126	/// Reads a (potentially unaligned) value from the view.
127	#[inline]
128	pub unsafe fn read_unchecked<T: Pod>(&self, offset: usize) -> T {
129		let index = offset..offset + mem::size_of::<T>();
130		let bytes = self.bytes.get_unchecked(index);
131		let src = bytes.as_ptr() as *const T;
132		ptr::read_unaligned(src)
133	}
134}
135
136//----------------------------------------------------------------
137
138/// Reads a (potentially unaligned) value from the view into the destination.
139impl DataView {
140	/// Reads a (potentially unaligned) value from the view into the destination.
141	#[inline]
142	pub fn try_read_into<T: ?Sized + Pod>(&self, offset: usize, dest: &mut T) -> Option<()> {
143		let index = offset..offset + mem::size_of_val(dest);
144		let bytes = self.bytes.get(index)?;
145		unsafe {
146			let src = bytes.as_ptr();
147			let dst = bytes_mut(dest).as_mut_ptr();
148			ptr::copy_nonoverlapping(src, dst, bytes.len());
149			Some(())
150		}
151	}
152	/// Reads a (potentially unaligned) value from the view into the destination.
153	#[track_caller]
154	#[inline]
155	pub fn read_into<T: ?Sized + Pod>(&self, offset: usize, dest: &mut T) {
156		match self.try_read_into(offset, dest) {
157			Some(()) => (),
158			None => invalid_offset(),
159		}
160	}
161	/// Reads a (potentially unaligned) value from the view into the destination.
162	#[inline]
163	pub unsafe fn read_into_unchecked<T: ?Sized + Pod>(&self, offset: usize, dest: &mut T) {
164		let index = offset..offset + mem::size_of_val(dest);
165		let bytes = self.bytes.get_unchecked(index);
166		let src = bytes.as_ptr();
167		let dst = bytes_mut(dest).as_mut_ptr();
168		ptr::copy_nonoverlapping(src, dst, bytes.len());
169	}
170}
171
172//----------------------------------------------------------------
173
174/// Gets an aligned reference into the view.
175impl DataView {
176	/// Gets an aligned reference into the view.
177	#[inline]
178	pub fn try_get<T: Pod>(&self, offset: usize) -> Option<&T> {
179		let index = offset..offset + mem::size_of::<T>();
180		let bytes = self.bytes.get(index)?;
181		let unaligned_ptr = bytes.as_ptr() as *const T;
182		if !is_aligned(unaligned_ptr) {
183			return None;
184		}
185		unsafe {
186			Some(&*unaligned_ptr)
187		}
188	}
189	/// Gets an aligned reference into the view.
190	#[track_caller]
191	#[inline]
192	pub fn get<T: Pod>(&self, offset: usize) -> &T {
193		match self.try_get(offset) {
194			Some(value) => value,
195			None => invalid_offset(),
196		}
197	}
198	/// Gets an aligned reference into the view.
199	#[inline]
200	pub unsafe fn get_unchecked<T: Pod>(&self, offset: usize) -> &T {
201		let index = offset..offset + mem::size_of::<T>();
202		let bytes = self.bytes.get_unchecked(index);
203		&*(bytes.as_ptr() as *const T)
204	}
205}
206
207//----------------------------------------------------------------
208
209/// Gets an aligned mutable reference into the view.
210impl DataView {
211	/// Gets an aligned mutable reference into the view.
212	#[inline]
213	pub fn try_get_mut<T: Pod>(&mut self, offset: usize) -> Option<&mut T> {
214		let index = offset..offset + mem::size_of::<T>();
215		let bytes = self.bytes.get_mut(index)?;
216		let unaligned_ptr = bytes.as_mut_ptr() as *mut T;
217		if !is_aligned(unaligned_ptr) {
218			return None;
219		}
220		unsafe {
221			Some(&mut *unaligned_ptr)
222		}
223	}
224	/// Gets an aligned mutable reference into the view.
225	#[track_caller]
226	#[inline]
227	pub fn get_mut<T: Pod>(&mut self, offset: usize) -> &mut T {
228		match self.try_get_mut(offset) {
229			Some(value) => value,
230			None => invalid_offset(),
231		}
232	}
233	/// Gets an aligned mutable reference into the view.
234	#[inline]
235	pub unsafe fn get_unchecked_mut<T: Pod>(&mut self, offset: usize) -> &mut T {
236		let index = offset..offset + mem::size_of::<T>();
237		let bytes = self.bytes.get_unchecked_mut(index);
238		&mut *(bytes.as_mut_ptr() as *mut T)
239	}
240}
241
242//----------------------------------------------------------------
243
244/// Gets an aligned slice into the view.
245impl DataView {
246	/// Gets an aligned slice into the view.
247	#[inline]
248	pub fn try_slice<T: Pod>(&self, offset: usize, len: usize) -> Option<&[T]> {
249		let index = offset..offset + usize::checked_mul(len, mem::size_of::<T>())?;
250		let bytes = self.bytes.get(index)?;
251		let unaligned_ptr = bytes.as_ptr() as *const T;
252		if !is_aligned(unaligned_ptr) {
253			return None;
254		}
255		unsafe {
256			Some(slice::from_raw_parts(unaligned_ptr, len))
257		}
258	}
259	/// Gets an aligned slice into the view.
260	#[track_caller]
261	#[inline]
262	pub fn slice<T: Pod>(&self, offset: usize, len: usize) -> &[T] {
263		match self.try_slice(offset, len) {
264			Some(value) => value,
265			None => invalid_offset(),
266		}
267	}
268	/// Gets an aligned slice into the view.
269	#[inline]
270	pub unsafe fn slice_unchecked<T: Pod>(&self, offset: usize, len: usize) -> &[T] {
271		let index = offset..offset + len * mem::size_of::<T>();
272		let bytes = self.bytes.get_unchecked(index);
273		slice::from_raw_parts(bytes.as_ptr() as *const T, len)
274	}
275}
276
277//----------------------------------------------------------------
278
279/// Gets an aligned mutable slice into the view.
280impl DataView {
281	/// Gets an aligned mutable slice into the view.
282	#[inline]
283	pub fn try_slice_mut<T: Pod>(&mut self, offset: usize, len: usize) -> Option<&mut [T]> {
284		let index = offset..offset + usize::checked_mul(len, mem::size_of::<T>())?;
285		let bytes = self.bytes.get_mut(index)?;
286		let unaligned_ptr = bytes.as_mut_ptr() as *mut T;
287		if !is_aligned(unaligned_ptr) {
288			return None;
289		}
290		unsafe {
291			Some(slice::from_raw_parts_mut(unaligned_ptr, len))
292		}
293	}
294	/// Gets an aligned mutable slice into the view.
295	#[track_caller]
296	#[inline]
297	pub fn slice_mut<T: Pod>(&mut self, offset: usize, len: usize) -> &mut [T] {
298		match self.try_slice_mut(offset, len) {
299			Some(value) => value,
300			None => invalid_offset(),
301		}
302	}
303	/// Gets an aligned mutable slice into the view.
304	#[inline]
305	pub unsafe fn slice_unchecked_mut<T: Pod>(&mut self, offset: usize, len: usize) -> &mut [T] {
306		let index = offset..offset + len * mem::size_of::<T>();
307		let bytes = self.bytes.get_unchecked_mut(index);
308		slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut T, len)
309	}
310}
311
312//----------------------------------------------------------------
313
314/// Writes a value into the view.
315impl DataView {
316	/// Writes a value into the view.
317	#[inline]
318	pub fn try_write<T: ?Sized + Pod>(&mut self, offset: usize, value: &T) -> Option<()> {
319		let index = offset..offset + mem::size_of_val(value);
320		let bytes = self.bytes.get_mut(index)?;
321		bytes.copy_from_slice(crate::bytes(value));
322		Some(())
323	}
324	/// Writes a value into the view.
325	#[track_caller]
326	#[inline]
327	pub fn write<T: ?Sized + Pod>(&mut self, offset: usize, value: &T) {
328		match self.try_write(offset, value) {
329			Some(()) => (),
330			None => invalid_offset(),
331		}
332	}
333	/// Writes a value into the view.
334	#[inline]
335	pub unsafe fn write_unchecked<T: ?Sized + Pod>(&mut self, offset: usize, value: &T) {
336		let index = offset..offset + mem::size_of_val(value);
337		let bytes = self.bytes.get_unchecked_mut(index);
338		ptr::copy_nonoverlapping(crate::bytes(value).as_ptr(), bytes.as_mut_ptr(), bytes.len());
339	}
340}
341
342//----------------------------------------------------------------
343
344impl DataView {
345	/// Index the DataView creating a subview.
346	#[inline]
347	pub fn index<R: ops::RangeBounds<usize>>(&self, range: R) -> Option<&DataView> {
348		let start = match range.start_bound() {
349			ops::Bound::Unbounded => 0,
350			ops::Bound::Included(&start) => start,
351			ops::Bound::Excluded(&start) => start + 1,
352		};
353		let end = match range.end_bound() {
354			ops::Bound::Unbounded => self.len(),
355			ops::Bound::Included(&end) => end + 1,
356			ops::Bound::Excluded(&end) => end,
357		};
358		let bytes = self.bytes.get(start..end)?;
359		Some(DataView::from(bytes))
360	}
361	/// Index the DataView creating a mutable subview.
362	#[inline]
363	pub fn index_mut<R: ops::RangeBounds<usize>>(&mut self, range: R) -> Option<&mut DataView> {
364		let start = match range.start_bound() {
365			ops::Bound::Unbounded => 0,
366			ops::Bound::Included(&start) => start,
367			ops::Bound::Excluded(&start) => start + 1,
368		};
369		let end = match range.end_bound() {
370			ops::Bound::Unbounded => self.len(),
371			ops::Bound::Included(&end) => end + 1,
372			ops::Bound::Excluded(&end) => end,
373		};
374		let bytes = self.bytes.get_mut(start..end)?;
375		Some(DataView::from_mut(bytes))
376	}
377}
378
379//----------------------------------------------------------------
380
381impl<R: ops::RangeBounds<usize>> ops::Index<R> for DataView {
382	type Output = DataView;
383	#[track_caller]
384	#[inline]
385	fn index(&self, range: R) -> &DataView {
386		match self.index(range) {
387			Some(value) => value,
388			None => invalid_offset(),
389		}
390	}
391}
392impl<R: ops::RangeBounds<usize>> ops::IndexMut<R> for DataView {
393	#[track_caller]
394	#[inline]
395	fn index_mut(&mut self, range: R) -> &mut DataView {
396		match self.index_mut(range) {
397			Some(value) => value,
398			None => invalid_offset(),
399		}
400	}
401}
402
403//----------------------------------------------------------------
404
405#[cold]
406#[track_caller]
407#[inline(never)]
408fn invalid_offset() -> ! {
409	panic!("invalid offset")
410}