compact_str/repr/
traits.rs

1use core::hint::unreachable_unchecked;
2
3use super::Repr;
4use crate::ToCompactStringError;
5
6const FALSE: Repr = Repr::const_new("false");
7const TRUE: Repr = Repr::const_new("true");
8
9/// Defines how to _efficiently_ create a [`Repr`] from `self`
10pub(crate) trait IntoRepr {
11    fn into_repr(self) -> Result<Repr, ToCompactStringError>;
12}
13
14impl IntoRepr for f32 {
15    #[inline]
16    fn into_repr(self) -> Result<Repr, ToCompactStringError> {
17        let mut buf = ryu::Buffer::new();
18        let s = buf.format(self);
19        Ok(Repr::new(s)?)
20    }
21}
22
23impl IntoRepr for f64 {
24    #[inline]
25    fn into_repr(self) -> Result<Repr, ToCompactStringError> {
26        let mut buf = ryu::Buffer::new();
27        let s = buf.format(self);
28        Ok(Repr::new(s)?)
29    }
30}
31
32impl IntoRepr for bool {
33    #[inline]
34    fn into_repr(self) -> Result<Repr, ToCompactStringError> {
35        if self {
36            Ok(TRUE)
37        } else {
38            Ok(FALSE)
39        }
40    }
41}
42
43impl IntoRepr for char {
44    #[inline]
45    fn into_repr(self) -> Result<Repr, ToCompactStringError> {
46        let mut buf = [0_u8; 4];
47        let s = self.encode_utf8(&mut buf);
48
49        // This match is just a hint for the compiler.
50        match s.len() {
51            1..=4 => (),
52            // SAFETY: a UTF-8 character is 1 to 4 bytes.
53            _ => unsafe { unreachable_unchecked() },
54        }
55
56        Ok(Repr::new(s)?)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use alloc::string::ToString;
63
64    use quickcheck_macros::quickcheck;
65
66    use super::IntoRepr;
67
68    #[test]
69    fn test_into_repr_bool() {
70        let t = true;
71        let repr = t.into_repr().unwrap();
72        assert_eq!(repr.as_str(), t.to_string());
73
74        let f = false;
75        let repr = f.into_repr().unwrap();
76        assert_eq!(repr.as_str(), f.to_string());
77    }
78
79    #[quickcheck]
80    #[cfg_attr(miri, ignore)]
81    fn quickcheck_into_repr_char(val: char) {
82        let repr = char::into_repr(val).unwrap();
83        assert_eq!(repr.as_str(), val.to_string());
84    }
85
86    #[test]
87    fn test_into_repr_f64_sanity() {
88        let vals = [
89            f64::MIN,
90            f64::MIN_POSITIVE,
91            f64::MAX,
92            f64::NEG_INFINITY,
93            f64::INFINITY,
94        ];
95
96        for x in &vals {
97            let repr = f64::into_repr(*x).unwrap();
98            let roundtrip = repr.as_str().parse::<f64>().unwrap();
99
100            assert_eq!(*x, roundtrip);
101        }
102    }
103
104    #[test]
105    fn test_into_repr_f64_nan() {
106        let repr = f64::into_repr(f64::NAN).unwrap();
107        let roundtrip = repr.as_str().parse::<f64>().unwrap();
108        assert!(roundtrip.is_nan());
109    }
110
111    #[quickcheck]
112    #[cfg_attr(miri, ignore)]
113    fn quickcheck_into_repr_f64(val: f64) {
114        let repr = f64::into_repr(val).unwrap();
115        let roundtrip = repr.as_str().parse::<f64>().unwrap();
116
117        // Note: The formatting of floats by `ryu` sometimes differs from that of `std`, so instead
118        // of asserting equality with `std` we just make sure the value roundtrips
119
120        if val.is_nan() != roundtrip.is_nan() {
121            assert_eq!(val, roundtrip);
122        }
123    }
124
125    // `f32` formatting is broken on powerpc64le, not only in `ryu` but also `std`
126    //
127    // See: https://github.com/rust-lang/rust/issues/96306
128    #[test]
129    #[cfg_attr(all(target_arch = "powerpc64", target_pointer_width = "64"), ignore)]
130    fn test_into_repr_f32_sanity() {
131        let vals = [
132            f32::MIN,
133            f32::MIN_POSITIVE,
134            f32::MAX,
135            f32::NEG_INFINITY,
136            f32::INFINITY,
137        ];
138
139        for x in &vals {
140            let repr = f32::into_repr(*x).unwrap();
141            let roundtrip = repr.as_str().parse::<f32>().unwrap();
142
143            assert_eq!(*x, roundtrip);
144        }
145    }
146
147    #[test]
148    #[cfg_attr(all(target_arch = "powerpc64", target_pointer_width = "64"), ignore)]
149    fn test_into_repr_f32_nan() {
150        let repr = f32::into_repr(f32::NAN).unwrap();
151        let roundtrip = repr.as_str().parse::<f32>().unwrap();
152        assert!(roundtrip.is_nan());
153    }
154
155    #[quickcheck]
156    #[cfg_attr(all(target_arch = "powerpc64", target_pointer_width = "64"), ignore)]
157    fn proptest_into_repr_f32(val: f32) {
158        let repr = f32::into_repr(val).unwrap();
159        let roundtrip = repr.as_str().parse::<f32>().unwrap();
160
161        // Note: The formatting of floats by `ryu` sometimes differs from that of `std`, so instead
162        // of asserting equality with `std` we just make sure the value roundtrips
163
164        if val.is_nan() != roundtrip.is_nan() {
165            assert_eq!(val, roundtrip);
166        }
167    }
168}