Skip to content

Document all unsafe usages. #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions src/ascii_char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ impl AsciiChar {
#[inline]
#[must_use]
pub unsafe fn from_ascii_unchecked(ch: u8) -> Self {
ch.to_ascii_char_unchecked()
// SAFETY: Caller guarantees `ch` is within bounds of ascii.
unsafe { ch.to_ascii_char_unchecked() }
}

/// Converts an ASCII character into a `u8`.
Expand Down Expand Up @@ -659,12 +660,20 @@ impl AsciiChar {
/// ```
#[must_use]
pub fn as_printable_char(self) -> char {
unsafe {
match self as u8 {
b' '..=b'~' => self.as_char(),
127 => '␡',
_ => char::from_u32_unchecked(self as u32 + '␀' as u32),
}
match self as u8 {
// Non printable characters
// SAFETY: From codepoint 0x2400 ('␀') to 0x241f (`␟`), there are characters representing
// the unprintable characters from 0x0 to 0x1f, ordered correctly.
// As `b` is guaranteed to be within 0x0 to 0x1f, the conversion represents a
// valid character.
b @ 0x0..=0x1f => unsafe { char::from_u32_unchecked(u32::from('␀') + u32::from(b)) },

// 0x7f (delete) has it's own character at codepoint 0x2420, not 0x247f, so it is special
// cased to return it's character
0x7f => '␡',

// All other characters are printable, and per function contract use `Self::as_char`
_ => self.as_char(),
}
}

Expand Down Expand Up @@ -825,6 +834,14 @@ pub trait ToAsciiChar {
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError>;

/// Convert to `AsciiChar` without checking that it is an ASCII character.
///
/// # Safety
/// Calling this function with a value outside of the ascii range, `0x0` to `0x7f` inclusive,
/// is undefined behavior.
// TODO: Make sure this is the contract we want to express in this function.
// It is ambigous if numbers such as `0xffffff20_u32` are valid ascii characters,
// as this function returns `Ascii::Space` due to the cast to `u8`, even though
// `to_ascii_char` returns `Err()`.
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar;
}

Expand All @@ -833,6 +850,7 @@ impl ToAsciiChar for AsciiChar {
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
Ok(self)
}

#[inline]
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
self
Expand All @@ -846,7 +864,10 @@ impl ToAsciiChar for u8 {
}
#[inline]
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
mem::transmute(self)
// SAFETY: Caller guarantees `self` is within bounds of the enum
// variants, so this cast successfully produces a valid ascii
// variant
unsafe { mem::transmute::<u8, AsciiChar>(self) }
}
}

Expand All @@ -857,34 +878,38 @@ impl ToAsciiChar for u8 {
impl ToAsciiChar for i8 {
#[inline]
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
(self as u32).to_ascii_char()
u32::from(self as u8).to_ascii_char()
}
#[inline]
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
mem::transmute(self)
// SAFETY: Caller guarantees `self` is within bounds of the enum
// variants, so this cast successfully produces a valid ascii
// variant
unsafe { mem::transmute::<u8, AsciiChar>(self as u8) }
}
}

impl ToAsciiChar for char {
#[inline]
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
(self as u32).to_ascii_char()
u32::from(self).to_ascii_char()
}
#[inline]
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
(self as u32).to_ascii_char_unchecked()
// SAFETY: Caller guarantees we're within ascii range.
unsafe { u32::from(self).to_ascii_char_unchecked() }
}
}

impl ToAsciiChar for u32 {
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
unsafe {
match self {
0..=127 => Ok(self.to_ascii_char_unchecked()),
_ => Err(ToAsciiCharError(())),
}
match self {
// SAFETY: We're within the valid ascii range in this branch.
0x0..=0x7f => Ok(unsafe { self.to_ascii_char_unchecked() }),
_ => Err(ToAsciiCharError(())),
}
}

#[inline]
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
// Note: This cast discards the top bytes, this may cause problems, see
Expand Down
Loading