Skip to content

Commit 92512d6

Browse files
author
Thomas Bahn
authored
Merge pull request #72 from Zenithsiz/unsafe-doc
Document all `unsafe` usages.
2 parents 5bf5bcd + 926dec3 commit 92512d6

File tree

4 files changed

+192
-97
lines changed

4 files changed

+192
-97
lines changed

src/ascii_char.rs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@ impl AsciiChar {
376376
#[inline]
377377
#[must_use]
378378
pub unsafe fn from_ascii_unchecked(ch: u8) -> Self {
379-
ch.to_ascii_char_unchecked()
379+
// SAFETY: Caller guarantees `ch` is within bounds of ascii.
380+
unsafe { ch.to_ascii_char_unchecked() }
380381
}
381382

382383
/// Converts an ASCII character into a `u8`.
@@ -659,12 +660,20 @@ impl AsciiChar {
659660
/// ```
660661
#[must_use]
661662
pub fn as_printable_char(self) -> char {
662-
unsafe {
663-
match self as u8 {
664-
b' '..=b'~' => self.as_char(),
665-
127 => '␡',
666-
_ => char::from_u32_unchecked(self as u32 + '␀' as u32),
667-
}
663+
match self as u8 {
664+
// Non printable characters
665+
// SAFETY: From codepoint 0x2400 ('␀') to 0x241f (`␟`), there are characters representing
666+
// the unprintable characters from 0x0 to 0x1f, ordered correctly.
667+
// As `b` is guaranteed to be within 0x0 to 0x1f, the conversion represents a
668+
// valid character.
669+
b @ 0x0..=0x1f => unsafe { char::from_u32_unchecked(u32::from('␀') + u32::from(b)) },
670+
671+
// 0x7f (delete) has it's own character at codepoint 0x2420, not 0x247f, so it is special
672+
// cased to return it's character
673+
0x7f => '␡',
674+
675+
// All other characters are printable, and per function contract use `Self::as_char`
676+
_ => self.as_char(),
668677
}
669678
}
670679

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

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

@@ -833,6 +850,7 @@ impl ToAsciiChar for AsciiChar {
833850
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
834851
Ok(self)
835852
}
853+
836854
#[inline]
837855
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
838856
self
@@ -846,7 +864,10 @@ impl ToAsciiChar for u8 {
846864
}
847865
#[inline]
848866
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
849-
mem::transmute(self)
867+
// SAFETY: Caller guarantees `self` is within bounds of the enum
868+
// variants, so this cast successfully produces a valid ascii
869+
// variant
870+
unsafe { mem::transmute::<u8, AsciiChar>(self) }
850871
}
851872
}
852873

@@ -857,34 +878,38 @@ impl ToAsciiChar for u8 {
857878
impl ToAsciiChar for i8 {
858879
#[inline]
859880
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
860-
(self as u32).to_ascii_char()
881+
u32::from(self as u8).to_ascii_char()
861882
}
862883
#[inline]
863884
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
864-
mem::transmute(self)
885+
// SAFETY: Caller guarantees `self` is within bounds of the enum
886+
// variants, so this cast successfully produces a valid ascii
887+
// variant
888+
unsafe { mem::transmute::<u8, AsciiChar>(self as u8) }
865889
}
866890
}
867891

868892
impl ToAsciiChar for char {
869893
#[inline]
870894
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
871-
(self as u32).to_ascii_char()
895+
u32::from(self).to_ascii_char()
872896
}
873897
#[inline]
874898
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
875-
(self as u32).to_ascii_char_unchecked()
899+
// SAFETY: Caller guarantees we're within ascii range.
900+
unsafe { u32::from(self).to_ascii_char_unchecked() }
876901
}
877902
}
878903

879904
impl ToAsciiChar for u32 {
880905
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
881-
unsafe {
882-
match self {
883-
0..=127 => Ok(self.to_ascii_char_unchecked()),
884-
_ => Err(ToAsciiCharError(())),
885-
}
906+
match self {
907+
// SAFETY: We're within the valid ascii range in this branch.
908+
0x0..=0x7f => Ok(unsafe { self.to_ascii_char_unchecked() }),
909+
_ => Err(ToAsciiCharError(())),
886910
}
887911
}
912+
888913
#[inline]
889914
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
890915
// Note: This cast discards the top bytes, this may cause problems, see

0 commit comments

Comments
 (0)