Skip to content

Commit 2a24f7d

Browse files
committed
core: implement Add for ascii::Char; drop ascii::Char::digit
Implement Add for ascii::Char which panics in debug builds if sum isn’t an ASCII character and wraps the result in release builds. This covers use cases of digit and digit_unchecked methods so remove them as well. Those methods can now be replaced by simple addition: use core::ascii; assert_eq!(ascii::Char::Digit8, ascii::Char::Digit0 + 8); Issue: #110998
1 parent cd6d8f2 commit 2a24f7d

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

library/core/src/ascii/ascii_char.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use crate::fmt::{self, Write};
77
use crate::mem::transmute;
8+
use crate::ops::{Add, AddAssign};
89

910
/// One of the 128 Unicode characters from U+0000 through U+007F,
1011
/// often known as the [ASCII] subset.
@@ -557,6 +558,14 @@ impl [AsciiChar] {
557558
}
558559
}
559560

561+
#[unstable(feature = "ascii_char", issue = "110998")]
562+
impl From<AsciiChar> for u8 {
563+
#[inline]
564+
fn from(chr: AsciiChar) -> Self {
565+
chr as u8
566+
}
567+
}
568+
560569
#[unstable(feature = "ascii_char", issue = "110998")]
561570
impl fmt::Display for AsciiChar {
562571
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -600,3 +609,59 @@ impl fmt::Debug for AsciiChar {
600609
f.write_char('\'')
601610
}
602611
}
612+
613+
#[unstable(feature = "ascii_char", issue = "110998")]
614+
impl Add<u8> for AsciiChar {
615+
type Output = AsciiChar;
616+
617+
/// Calculates sum of the ASCII value and given offset.
618+
///
619+
/// In debug builds, panics if result is greater than largest ASCII value.
620+
///
621+
/// In release builds wraps the value (i.e. masks out the most significant
622+
/// bit) so the output is a valid ASCII character.
623+
///
624+
/// ```
625+
/// #![feature(ascii_char, ascii_char_variants)]
626+
/// use core::ascii::Char;
627+
///
628+
/// assert_eq!(Char::Digit8, Char::Digit0 + 8);
629+
/// ```
630+
#[inline]
631+
fn add(self, rhs: u8) -> Self::Output {
632+
add_impl(self, rhs)
633+
}
634+
}
635+
636+
#[unstable(feature = "ascii_char", issue = "110998")]
637+
impl Add<AsciiChar> for u8 {
638+
type Output = AsciiChar;
639+
640+
#[inline]
641+
fn add(self, rhs: AsciiChar) -> Self::Output {
642+
add_impl(rhs, self)
643+
}
644+
}
645+
646+
#[unstable(feature = "ascii_char", issue = "110998")]
647+
impl AddAssign<u8> for AsciiChar {
648+
#[inline]
649+
fn add_assign(&mut self, rhs: u8) {
650+
*self = add_impl(*self, rhs)
651+
}
652+
}
653+
654+
forward_ref_binop! { impl Add, add for AsciiChar, u8 }
655+
forward_ref_binop! { impl Add, add for u8, AsciiChar }
656+
forward_ref_op_assign! { impl AddAssign, add_assign for AsciiChar, u8 }
657+
658+
#[inline]
659+
fn add_impl(chr: AsciiChar, rhs: u8) -> AsciiChar {
660+
let sum = u16::from(u8::from(chr)) + u16::from(rhs);
661+
if !cfg!(debug_assertions) || sum < 128 {
662+
// SAFETY: `& 127` limits the sum to a valid ASCII value.
663+
unsafe { AsciiChar::from_u8_unchecked((sum as u8) & 127) }
664+
} else {
665+
panic!("{} + {} overflows ASCII value", u8::from(chr), rhs)
666+
}
667+
}

library/core/tests/ascii_char.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use core::ascii::Char;
2+
3+
/// Tests addition of u8 values to ascii::Char;
4+
#[test]
5+
fn test_arithmetic_ok() {
6+
assert_eq!(Char::Digit8, Char::Digit0 + 8);
7+
assert_eq!(Char::Colon, Char::Digit0 + 10);
8+
assert_eq!(Char::Digit8, 8 + Char::Digit0);
9+
assert_eq!(Char::Colon, 10 + Char::Digit0);
10+
11+
let mut digit = Char::Digit0;
12+
digit += 8;
13+
assert_eq!(Char::Digit8, digit);
14+
}
15+
16+
/// Tests addition resulting in invalid ASCII character results in a panic.
17+
///
18+
/// Note: In release builds the result is wrapped instead of panicking. Since
19+
/// everything is built in debug mode when testing, this behaviour is not
20+
/// currently tested.
21+
#[test]
22+
#[should_panic]
23+
fn test_arithmetic_non_ascii() {
24+
let _ = Char::Digit0 + 120;
25+
}
26+
27+
/// Tests addition overflowing u8.
28+
///
29+
/// Note: In release builds the result is wrapped instead of panicking. Since
30+
/// everything is built in debug mode when testing, this behaviour is not
31+
/// currently tested.
32+
#[test]
33+
#[should_panic]
34+
fn test_arithmetic_overflow() {
35+
let _ = Char::Digit0 + 250;
36+
}

library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ mod alloc;
126126
mod any;
127127
mod array;
128128
mod ascii;
129+
mod ascii_char;
129130
mod asserting;
130131
mod async_iter;
131132
mod atomic;

0 commit comments

Comments
 (0)