• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // This file is part of ICU4X. For terms of use, please see the file
2 // called LICENSE at the top level of the ICU4X source tree
3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4 
5 use crate::*;
6 use core::fmt;
7 
8 macro_rules! impl_write_num {
9     ($u:ty, $i:ty, $test:ident) => {
10         impl $crate::Writeable for $u {
11             fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
12                 const MAX_LEN: usize = <$u>::MAX.ilog10() as usize + 1;
13                 let mut buf = [b'0'; MAX_LEN];
14                 let mut n = *self;
15                 let mut i = MAX_LEN;
16                 #[allow(clippy::indexing_slicing)] // n < 10^i
17                 while n != 0 {
18                     i -= 1;
19                     buf[i] = b'0' + (n % 10) as u8;
20                     n /= 10;
21                 }
22                 if i == MAX_LEN {
23                     debug_assert_eq!(*self, 0);
24                     i -= 1;
25                 }
26                 #[allow(clippy::indexing_slicing)] // buf is ASCII
27                 let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
28                 sink.write_str(s)
29             }
30 
31             fn writeable_length_hint(&self) -> $crate::LengthHint {
32                 LengthHint::exact(self.checked_ilog10().unwrap_or(0) as usize + 1)
33             }
34         }
35 
36         impl $crate::Writeable for $i {
37             fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
38                 if self.is_negative() {
39                     sink.write_str("-")?;
40                 }
41                 self.unsigned_abs().write_to(sink)
42             }
43 
44             fn writeable_length_hint(&self) -> $crate::LengthHint {
45                 $crate::LengthHint::exact(if self.is_negative() { 1 } else { 0 })
46                     + self.unsigned_abs().writeable_length_hint()
47             }
48         }
49 
50         #[test]
51         fn $test() {
52             use $crate::assert_writeable_eq;
53             assert_writeable_eq!(&(0 as $u), "0");
54             assert_writeable_eq!(&(0 as $i), "0");
55             assert_writeable_eq!(&(-0 as $i), "0");
56             assert_writeable_eq!(&(1 as $u), "1");
57             assert_writeable_eq!(&(1 as $i), "1");
58             assert_writeable_eq!(&(-1 as $i), "-1");
59             assert_writeable_eq!(&(9 as $u), "9");
60             assert_writeable_eq!(&(9 as $i), "9");
61             assert_writeable_eq!(&(-9 as $i), "-9");
62             assert_writeable_eq!(&(10 as $u), "10");
63             assert_writeable_eq!(&(10 as $i), "10");
64             assert_writeable_eq!(&(-10 as $i), "-10");
65             assert_writeable_eq!(&(99 as $u), "99");
66             assert_writeable_eq!(&(99 as $i), "99");
67             assert_writeable_eq!(&(-99 as $i), "-99");
68             assert_writeable_eq!(&(100 as $u), "100");
69             assert_writeable_eq!(&(-100 as $i), "-100");
70             assert_writeable_eq!(&<$u>::MAX, <$u>::MAX.to_string());
71             assert_writeable_eq!(&<$i>::MAX, <$i>::MAX.to_string());
72             assert_writeable_eq!(&<$i>::MIN, <$i>::MIN.to_string());
73 
74             use rand::{rngs::SmallRng, Rng, SeedableRng};
75             let mut rng = SmallRng::seed_from_u64(4); // chosen by fair dice roll.
76                                                       // guaranteed to be random.
77             for _ in 0..1000 {
78                 let rand = rng.gen::<$u>();
79                 assert_writeable_eq!(rand, rand.to_string());
80             }
81         }
82     };
83 }
84 
85 impl_write_num!(u8, i8, test_u8);
86 impl_write_num!(u16, i16, test_u16);
87 impl_write_num!(u32, i32, test_u32);
88 impl_write_num!(u64, i64, test_u64);
89 impl_write_num!(u128, i128, test_u128);
90 impl_write_num!(usize, isize, test_usize);
91 
92 impl Writeable for str {
93     #[inline]
write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result94     fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
95         sink.write_str(self)
96     }
97 
98     #[inline]
writeable_length_hint(&self) -> LengthHint99     fn writeable_length_hint(&self) -> LengthHint {
100         LengthHint::exact(self.len())
101     }
102 
103     /// Returns a borrowed `str`.
104     ///
105     /// # Examples
106     ///
107     /// ```
108     /// use std::borrow::Cow;
109     /// use writeable::Writeable;
110     ///
111     /// let cow = "foo".write_to_string();
112     /// assert!(matches!(cow, Cow::Borrowed(_)));
113     /// ```
114     #[inline]
write_to_string(&self) -> Cow<str>115     fn write_to_string(&self) -> Cow<str> {
116         Cow::Borrowed(self)
117     }
118 }
119 
120 impl Writeable for String {
121     #[inline]
write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result122     fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
123         sink.write_str(self)
124     }
125 
126     #[inline]
writeable_length_hint(&self) -> LengthHint127     fn writeable_length_hint(&self) -> LengthHint {
128         LengthHint::exact(self.len())
129     }
130 
131     #[inline]
write_to_string(&self) -> Cow<str>132     fn write_to_string(&self) -> Cow<str> {
133         Cow::Borrowed(self)
134     }
135 }
136 
137 impl Writeable for char {
138     #[inline]
write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result139     fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
140         sink.write_char(*self)
141     }
142 
143     #[inline]
writeable_length_hint(&self) -> LengthHint144     fn writeable_length_hint(&self) -> LengthHint {
145         LengthHint::exact(self.len_utf8())
146     }
147 
148     #[inline]
write_to_string(&self) -> Cow<str>149     fn write_to_string(&self) -> Cow<str> {
150         let mut s = String::with_capacity(self.len_utf8());
151         s.push(*self);
152         Cow::Owned(s)
153     }
154 }
155 
156 impl<T: Writeable + ?Sized> Writeable for &T {
157     #[inline]
write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result158     fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
159         (*self).write_to(sink)
160     }
161 
162     #[inline]
write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result163     fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
164         (*self).write_to_parts(sink)
165     }
166 
167     #[inline]
writeable_length_hint(&self) -> LengthHint168     fn writeable_length_hint(&self) -> LengthHint {
169         (*self).writeable_length_hint()
170     }
171 
172     #[inline]
write_to_string(&self) -> Cow<str>173     fn write_to_string(&self) -> Cow<str> {
174         (*self).write_to_string()
175     }
176 }
177 
178 macro_rules! impl_write_smart_pointer {
179     ($ty:path, T: $extra_bound:path) => {
180         impl<'a, T: ?Sized + Writeable + $extra_bound> Writeable for $ty {
181             #[inline]
182             fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
183                 core::borrow::Borrow::<T>::borrow(self).write_to(sink)
184             }
185             #[inline]
186             fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
187                 core::borrow::Borrow::<T>::borrow(self).write_to_parts(sink)
188             }
189             #[inline]
190             fn writeable_length_hint(&self) -> LengthHint {
191                 core::borrow::Borrow::<T>::borrow(self).writeable_length_hint()
192             }
193             #[inline]
194             fn write_to_string(&self) -> Cow<str> {
195                 core::borrow::Borrow::<T>::borrow(self).write_to_string()
196             }
197         }
198     };
199     ($ty:path) => {
200         // Add a harmless duplicate Writeable bound
201         impl_write_smart_pointer!($ty, T: Writeable);
202     };
203 }
204 
205 impl_write_smart_pointer!(Cow<'a, T>, T: alloc::borrow::ToOwned);
206 impl_write_smart_pointer!(alloc::boxed::Box<T>);
207 impl_write_smart_pointer!(alloc::rc::Rc<T>);
208 impl_write_smart_pointer!(alloc::sync::Arc<T>);
209 
210 #[test]
test_string_impls()211 fn test_string_impls() {
212     fn check_writeable_slice<W: Writeable + core::fmt::Display>(writeables: &[W]) {
213         assert_writeable_eq!(&writeables[0], "");
214         assert_writeable_eq!(&writeables[1], "abc");
215         assert!(matches!(writeables[0].write_to_string(), Cow::Borrowed(_)));
216         assert!(matches!(writeables[1].write_to_string(), Cow::Borrowed(_)));
217     }
218 
219     // test str impl
220     let arr: &[&str] = &["", "abc"];
221     check_writeable_slice(arr);
222 
223     // test String impl
224     let arr: &[String] = &[String::new(), "abc".to_owned()];
225     check_writeable_slice(arr);
226 
227     // test char impl
228     let chars = ['a', 'β', '你', '��'];
229     for i in 0..chars.len() {
230         let s = String::from(chars[i]);
231         assert_writeable_eq!(&chars[i], s);
232         for j in 0..chars.len() {
233             assert_eq!(
234                 crate::cmp_str(&chars[j], &s),
235                 chars[j].cmp(&chars[i]),
236                 "{:?} vs {:?}",
237                 chars[j],
238                 chars[i]
239             );
240         }
241     }
242 
243     // test Cow impl
244     let arr: &[Cow<str>] = &[Cow::Borrowed(""), Cow::Owned("abc".to_string())];
245     check_writeable_slice(arr);
246 
247     // test Box impl
248     let arr: &[Box<str>] = &["".into(), "abc".into()];
249     check_writeable_slice(arr);
250 
251     // test Rc impl
252     let arr: &[alloc::rc::Rc<str>] = &["".into(), "abc".into()];
253     check_writeable_slice(arr);
254 
255     // test Arc impl
256     let arr: &[alloc::sync::Arc<str>] = &["".into(), "abc".into()];
257     check_writeable_slice(arr);
258 
259     // test &T impl
260     let arr: &[&String] = &[&String::new(), &"abc".to_owned()];
261     check_writeable_slice(arr);
262 }
263