@@ -376,7 +376,8 @@ impl AsciiChar {
376
376
#[ inline]
377
377
#[ must_use]
378
378
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 ( ) }
380
381
}
381
382
382
383
/// Converts an ASCII character into a `u8`.
@@ -659,12 +660,20 @@ impl AsciiChar {
659
660
/// ```
660
661
#[ must_use]
661
662
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 ( ) ,
668
677
}
669
678
}
670
679
@@ -825,6 +834,14 @@ pub trait ToAsciiChar {
825
834
fn to_ascii_char ( self ) -> Result < AsciiChar , ToAsciiCharError > ;
826
835
827
836
/// 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()`.
828
845
unsafe fn to_ascii_char_unchecked ( self ) -> AsciiChar ;
829
846
}
830
847
@@ -833,6 +850,7 @@ impl ToAsciiChar for AsciiChar {
833
850
fn to_ascii_char ( self ) -> Result < AsciiChar , ToAsciiCharError > {
834
851
Ok ( self )
835
852
}
853
+
836
854
#[ inline]
837
855
unsafe fn to_ascii_char_unchecked ( self ) -> AsciiChar {
838
856
self
@@ -846,7 +864,10 @@ impl ToAsciiChar for u8 {
846
864
}
847
865
#[ inline]
848
866
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 ) }
850
871
}
851
872
}
852
873
@@ -857,34 +878,38 @@ impl ToAsciiChar for u8 {
857
878
impl ToAsciiChar for i8 {
858
879
#[ inline]
859
880
fn to_ascii_char ( self ) -> Result < AsciiChar , ToAsciiCharError > {
860
- ( self as u32 ) . to_ascii_char ( )
881
+ u32 :: from ( self as u8 ) . to_ascii_char ( )
861
882
}
862
883
#[ inline]
863
884
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 ) }
865
889
}
866
890
}
867
891
868
892
impl ToAsciiChar for char {
869
893
#[ inline]
870
894
fn to_ascii_char ( self ) -> Result < AsciiChar , ToAsciiCharError > {
871
- ( self as u32 ) . to_ascii_char ( )
895
+ u32 :: from ( self ) . to_ascii_char ( )
872
896
}
873
897
#[ inline]
874
898
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 ( ) }
876
901
}
877
902
}
878
903
879
904
impl ToAsciiChar for u32 {
880
905
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 ( ( ) ) ) ,
886
910
}
887
911
}
912
+
888
913
#[ inline]
889
914
unsafe fn to_ascii_char_unchecked ( self ) -> AsciiChar {
890
915
// Note: This cast discards the top bytes, this may cause problems, see
0 commit comments