Skip to content

Commit 93608b2

Browse files
authored
Merge pull request #539 from nicholasbishop/bishop-unaligned-slice-1
Replace `UnalignedCStr16` with `UnalignedSlice`
2 parents 80065b5 + 85a48e5 commit 93608b2

File tree

6 files changed

+304
-112
lines changed

6 files changed

+304
-112
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
- Added `PhysicalAddress` and `VirtualAddress` type aliases.
88
- Added `Guid::from_bytes` and `Guid::to_bytes`.
9+
- Added `UnalignedSlice` for representing a reference to an unaligned
10+
slice.
911

1012
### Changed
1113

@@ -22,6 +24,12 @@
2224
- The `Revision` type now implements `Display` with correct formatting
2325
for all UEFI versions. The custom `Debug` impl has been removed and
2426
replaced with a derived `Debug` impl.
27+
28+
### Removed
29+
30+
- Removed `UnalignedCStr16`; use `UnalignedSlice` instead. An
31+
`UnalignedSlice<u16>` can be converted to a string with `to_cstr16` or
32+
`to_cstring16`.
2533

2634
## uefi-macros - [Unreleased]
2735

src/data_types/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,17 @@ mod enums;
129129

130130
mod strs;
131131
pub use self::strs::{
132-
CStr16, CStr8, EqStrUntilNul, FromSliceWithNulError, FromStrWithBufError, UnalignedCStr16,
133-
UnalignedCStr16Error,
132+
CStr16, CStr8, EqStrUntilNul, FromSliceWithNulError, FromStrWithBufError, UnalignedCStr16Error,
134133
};
135134

136135
#[cfg(feature = "exts")]
137136
mod owned_strs;
138137
#[cfg(feature = "exts")]
139138
pub use self::owned_strs::{CString16, FromStrError};
140139

140+
mod unaligned_slice;
141+
pub use unaligned_slice::UnalignedSlice;
142+
141143
#[cfg(test)]
142144
mod tests {
143145
use super::*;

src/data_types/owned_strs.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::chars::{Char16, NUL_16};
22
use super::strs::{CStr16, FromSliceWithNulError};
33
use crate::alloc_api::vec::Vec;
44
use crate::data_types::strs::EqStrUntilNul;
5+
use crate::data_types::UnalignedSlice;
56
use core::fmt;
67
use core::ops;
78

@@ -85,6 +86,22 @@ impl TryFrom<Vec<u16>> for CString16 {
8586
}
8687
}
8788

89+
impl<'a> TryFrom<&UnalignedSlice<'a, u16>> for CString16 {
90+
type Error = FromSliceWithNulError;
91+
92+
fn try_from(input: &UnalignedSlice<u16>) -> Result<Self, Self::Error> {
93+
let v = input.to_vec();
94+
CString16::try_from(v)
95+
}
96+
}
97+
98+
impl<'a> UnalignedSlice<'a, u16> {
99+
/// Copies `self` to a new [`CString16`].
100+
pub fn to_cstring16(&self) -> Result<CString16, FromSliceWithNulError> {
101+
CString16::try_from(self)
102+
}
103+
}
104+
88105
impl ops::Deref for CString16 {
89106
type Target = CStr16;
90107

src/data_types/strs.rs

Lines changed: 36 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use super::chars::{Char16, Char8, NUL_16, NUL_8};
2+
use super::UnalignedSlice;
23
use core::fmt;
34
use core::iter::Iterator;
4-
use core::marker::PhantomData;
55
use core::mem::MaybeUninit;
66
use core::result::Result;
77
use core::slice;
8+
89
#[cfg(feature = "exts")]
9-
use {super::CString16, crate::alloc_api::vec::Vec};
10+
use super::CString16;
1011

1112
/// Errors which can occur during checked `[uN]` -> `CStrN` conversions
1213
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -21,7 +22,7 @@ pub enum FromSliceWithNulError {
2122
NotNulTerminated,
2223
}
2324

24-
/// Error returned by [`UnalignedCStr16::to_cstr16`].
25+
/// Error returned by [`CStr16::from_unaligned_slice`].
2526
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2627
pub enum UnalignedCStr16Error {
2728
/// An invalid character was encountered.
@@ -261,6 +262,31 @@ impl CStr16 {
261262
})
262263
}
263264

265+
/// Create a [`CStr16`] from an [`UnalignedSlice`] using an aligned
266+
/// buffer for storage. The lifetime of the output is tied to `buf`,
267+
/// not `src`.
268+
pub fn from_unaligned_slice<'buf>(
269+
src: &UnalignedSlice<'_, u16>,
270+
buf: &'buf mut [MaybeUninit<u16>],
271+
) -> Result<&'buf CStr16, UnalignedCStr16Error> {
272+
// The input `buf` might be longer than needed, so get a
273+
// subslice of the required length.
274+
let buf = buf
275+
.get_mut(..src.len())
276+
.ok_or(UnalignedCStr16Error::BufferTooSmall)?;
277+
278+
src.copy_to_maybe_uninit(buf);
279+
let buf = unsafe {
280+
// Safety: `copy_buf` fully initializes the slice.
281+
MaybeUninit::slice_assume_init_ref(buf)
282+
};
283+
CStr16::from_u16_with_nul(buf).map_err(|e| match e {
284+
FromSliceWithNulError::InvalidChar(v) => UnalignedCStr16Error::InvalidChar(v),
285+
FromSliceWithNulError::InteriorNul(v) => UnalignedCStr16Error::InteriorNul(v),
286+
FromSliceWithNulError::NotNulTerminated => UnalignedCStr16Error::NotNulTerminated,
287+
})
288+
}
289+
264290
/// Returns the inner pointer to this C string
265291
pub fn as_ptr(&self) -> *const Char16 {
266292
self.0.as_ptr()
@@ -378,110 +404,15 @@ impl PartialEq<CString16> for &CStr16 {
378404
}
379405
}
380406

381-
/// An unaligned UCS-2 null-terminated string.
382-
///
383-
/// This wrapper type can be used with UEFI strings that are inside a
384-
/// [`repr(packed)`] struct. Creating a reference to a packed field is
385-
/// not allowed because it might not be properly aligned, so a
386-
/// [`CStr16`] can't be directly constructed. `UnalignedCStr16` instead
387-
/// takes a pointer to the unaligned field, which is allowed. The
388-
/// resulting unaligned string cannot be used directly, but must be
389-
/// converted to an aligned form first with [`to_cstr16`] or
390-
/// [`to_cstring16`].
391-
///
392-
/// [`repr(packed)`]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked
393-
/// [`to_cstr16`]: Self::to_cstr16
394-
/// [`to_cstring16`]: Self::to_cstring16
395-
#[derive(Debug)]
396-
pub struct UnalignedCStr16<'a> {
397-
data: *const u16,
398-
len: usize,
399-
_phantom_lifetime: PhantomData<&'a ()>,
400-
}
401-
402-
// While it's not unsafe to have an empty `UnalignedCStr16`, there's not
403-
// much point either since the string wouldn't be valid without a null
404-
// terminator. So skip adding an `is_empty` method.
405-
#[allow(clippy::len_without_is_empty)]
406-
impl<'a> UnalignedCStr16<'a> {
407-
/// Create an `UnalignedCStr16` from a `*const u16` pointer. The
408-
/// pointer must be valid but can be unaligned. The `len` parameter
409-
/// is the number of `u16` characters in the string (not the number
410-
/// of bytes), including the trailing null.
411-
///
412-
/// The `_lifetime` parameter is used to make it easy to set an
413-
/// appropriate lifetime for `'a`. The caller should pass in a
414-
/// reference to something tied to the lifetime of `data`. (The
415-
/// `data` parameter cannot itself be a reference because the
416-
/// pointer is allowed to be unaligned.)
417-
///
418-
/// # Safety
419-
///
420-
/// The `data` pointer cannot be dangling, and must remain valid for
421-
/// the lifetime of `'a`. There must be at least `len` `u16`
422-
/// elements starting with the the first character pointed to by
423-
/// `data`. These restrictions allow all other methods on
424-
/// `UnalignedCStr16` to be safe.
425-
pub unsafe fn new<T: ?Sized>(_lifetime: &'a T, data: *const u16, len: usize) -> Self {
426-
Self {
427-
data,
428-
len,
429-
_phantom_lifetime: PhantomData,
430-
}
431-
}
432-
433-
/// Number of `u16` elements in the string, including the trailing null.
434-
pub fn len(&self) -> usize {
435-
self.len
436-
}
437-
438-
/// Copy the data to an aligned buffer. Panics if the length of
439-
/// `dst` is not exactly the same as `self.len()`. Otherwise the
440-
/// function always succeeds, and initializes all elements of `dst`.
441-
pub fn copy_to(&self, dst: &mut [MaybeUninit<u16>]) {
442-
if dst.len() != self.len {
443-
panic!("incorrect buffer length");
444-
}
445-
446-
for (i, elem) in dst.iter_mut().enumerate() {
447-
unsafe { elem.write(self.data.add(i).read_unaligned()) };
448-
}
449-
}
450-
451-
/// Convert to a [`CStr16`] using an aligned buffer for storage. The
452-
/// lifetime of the output is tied to `buf`, not `self`.
407+
impl<'a> UnalignedSlice<'a, u16> {
408+
/// Create a [`CStr16`] from an [`UnalignedSlice`] using an aligned
409+
/// buffer for storage. The lifetime of the output is tied to `buf`,
410+
/// not `self`.
453411
pub fn to_cstr16<'buf>(
454412
&self,
455413
buf: &'buf mut [MaybeUninit<u16>],
456414
) -> Result<&'buf CStr16, UnalignedCStr16Error> {
457-
// The input `buf` might be longer than needed, so get a
458-
// subslice of the required length.
459-
let buf = buf
460-
.get_mut(..self.len())
461-
.ok_or(UnalignedCStr16Error::BufferTooSmall)?;
462-
463-
self.copy_to(buf);
464-
let buf = unsafe {
465-
// Safety: `copy_buf` fully initializes the slice.
466-
MaybeUninit::slice_assume_init_ref(buf)
467-
};
468-
CStr16::from_u16_with_nul(buf).map_err(|e| match e {
469-
FromSliceWithNulError::InvalidChar(v) => UnalignedCStr16Error::InvalidChar(v),
470-
FromSliceWithNulError::InteriorNul(v) => UnalignedCStr16Error::InteriorNul(v),
471-
FromSliceWithNulError::NotNulTerminated => UnalignedCStr16Error::NotNulTerminated,
472-
})
473-
}
474-
475-
/// Convert to a [`CString16`]. Requires the `exts` feature.
476-
#[cfg(feature = "exts")]
477-
pub fn to_cstring16(&self) -> Result<CString16, FromSliceWithNulError> {
478-
let len = self.len();
479-
let mut v = Vec::with_capacity(len);
480-
unsafe {
481-
self.copy_to(v.spare_capacity_mut());
482-
v.set_len(len);
483-
}
484-
CString16::try_from(v)
415+
CStr16::from_unaligned_slice(self, buf)
485416
}
486417
}
487418

@@ -580,8 +511,8 @@ mod tests {
580511
ptr.add(3).write_unaligned(b't'.into());
581512
ptr.add(4).write_unaligned(b'\0'.into());
582513

583-
// Create the `UnalignedCStr16`.
584-
UnalignedCStr16::new(&buf, ptr, 5)
514+
// Create the `UnalignedSlice`.
515+
UnalignedSlice::new(ptr, 5)
585516
};
586517

587518
// Test `to_cstr16()` with too small of a buffer.

0 commit comments

Comments
 (0)