|
4 | 4 |
|
5 | 5 | use crate::{TagTrait, TagType, TagTypeId};
|
6 | 6 | use core::fmt;
|
7 |
| -use core::fmt::{Debug, Formatter}; |
| 7 | +use core::fmt::{Debug, Display, Formatter}; |
8 | 8 | use core::marker::PhantomData;
|
9 | 9 | use core::str::Utf8Error;
|
10 | 10 |
|
| 11 | +/// Error type describing failures when parsing the string from a tag. |
| 12 | +#[derive(Debug, PartialEq, Eq, Clone)] |
| 13 | +pub enum StringError { |
| 14 | + /// There is no terminating NUL character, although the specification |
| 15 | + /// requires one. |
| 16 | + MissingNul(core::ffi::FromBytesUntilNulError), |
| 17 | + /// The sequence until the first NUL character is not valid UTF-8. |
| 18 | + Utf8(Utf8Error), |
| 19 | +} |
| 20 | + |
| 21 | +impl Display for StringError { |
| 22 | + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
| 23 | + write!(f, "{:?}", self) |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +#[cfg(feature = "unstable")] |
| 28 | +impl core::error::Error for StringError { |
| 29 | + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { |
| 30 | + match self { |
| 31 | + StringError::MissingNul(e) => Some(e), |
| 32 | + StringError::Utf8(e) => Some(e), |
| 33 | + } |
| 34 | + } |
| 35 | +} |
| 36 | + |
11 | 37 | /// Common base structure for all tags that can be passed via the Multiboot2
|
12 | 38 | /// Information Structure (MBI) to a Multiboot2 payload/program/kernel.
|
13 | 39 | ///
|
@@ -38,22 +64,12 @@ impl Tag {
|
38 | 64 | unsafe { TagTrait::from_base_tag(self) }
|
39 | 65 | }
|
40 | 66 |
|
41 |
| - /// Some multiboot2 tags are a DST as they end with a dynamically sized byte |
42 |
| - /// slice. This function parses this slice as [`str`] so that either a valid |
43 |
| - /// UTF-8 Rust string slice without a terminating null byte or an error is |
44 |
| - /// returned. |
45 |
| - pub fn get_dst_str_slice(bytes: &[u8]) -> Result<&str, Utf8Error> { |
46 |
| - // Determine length of string before first NUL character |
47 |
| - |
48 |
| - let length = match bytes.iter().position(|ch| (*ch) == 0x00) { |
49 |
| - // Unterminated string case: |
50 |
| - None => bytes.len(), |
51 |
| - |
52 |
| - // Terminated case: |
53 |
| - Some(idx) => idx, |
54 |
| - }; |
55 |
| - // Convert to Rust string: |
56 |
| - core::str::from_utf8(&bytes[..length]) |
| 67 | + /// Parses the provided byte sequence as Multiboot string, which maps to a |
| 68 | + /// [`str`]. |
| 69 | + pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> { |
| 70 | + let cstr = core::ffi::CStr::from_bytes_until_nul(bytes).map_err(StringError::MissingNul)?; |
| 71 | + |
| 72 | + cstr.to_str().map_err(StringError::Utf8) |
57 | 73 | }
|
58 | 74 | }
|
59 | 75 |
|
@@ -128,21 +144,28 @@ mod tests {
|
128 | 144 | use super::*;
|
129 | 145 |
|
130 | 146 | #[test]
|
131 |
| - fn test_get_dst_str_slice() { |
132 |
| - // unlikely case |
133 |
| - assert_eq!(Ok(""), Tag::get_dst_str_slice(&[])); |
134 |
| - // also unlikely case |
135 |
| - assert_eq!(Ok(""), Tag::get_dst_str_slice(&[b'\0'])); |
136 |
| - // unlikely case: missing null byte. but the lib can cope with that |
137 |
| - assert_eq!(Ok("foobar"), Tag::get_dst_str_slice(b"foobar")); |
138 |
| - // test that the null bytes is not included in the string slice |
139 |
| - assert_eq!(Ok("foobar"), Tag::get_dst_str_slice(b"foobar\0")); |
140 |
| - // test that C-style null string termination works as expected |
141 |
| - assert_eq!(Ok("foo"), Tag::get_dst_str_slice(b"foo\0bar")); |
142 |
| - // test invalid utf8 |
| 147 | + fn parse_slice_as_string() { |
| 148 | + // empty slice is invalid |
| 149 | + assert!(matches!( |
| 150 | + Tag::parse_slice_as_string(&[]), |
| 151 | + Err(StringError::MissingNul(_)) |
| 152 | + )); |
| 153 | + // empty string is fine |
| 154 | + assert_eq!(Tag::parse_slice_as_string(&[0x00]), Ok("")); |
| 155 | + // reject invalid utf8 |
| 156 | + assert!(matches!( |
| 157 | + Tag::parse_slice_as_string(&[0xff, 0x00]), |
| 158 | + Err(StringError::Utf8(_)) |
| 159 | + )); |
| 160 | + // reject missing null |
143 | 161 | assert!(matches!(
|
144 |
| - Tag::get_dst_str_slice(&[0xff, 0xff]), |
145 |
| - Err(Utf8Error { .. }) |
| 162 | + Tag::parse_slice_as_string(b"hello"), |
| 163 | + Err(StringError::MissingNul(_)) |
146 | 164 | ));
|
| 165 | + // must not include final null |
| 166 | + assert_eq!(Tag::parse_slice_as_string(b"hello\0"), Ok("hello")); |
| 167 | + assert_eq!(Tag::parse_slice_as_string(b"hello\0\0"), Ok("hello")); |
| 168 | + // must skip everytihng after first null |
| 169 | + assert_eq!(Tag::parse_slice_as_string(b"hello\0foo"), Ok("hello")); |
147 | 170 | }
|
148 | 171 | }
|
0 commit comments