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 ] |
94 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
95 | sink.write_str(self) |
96 | } |
97 | |
98 | #[inline ] |
99 | 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 ] |
115 | fn write_to_string(&self) -> Cow<str> { |
116 | Cow::Borrowed(self) |
117 | } |
118 | |
119 | #[inline ] |
120 | fn writeable_cmp_bytes(&self, other: &[u8]) -> core::cmp::Ordering { |
121 | self.as_bytes().cmp(other) |
122 | } |
123 | } |
124 | |
125 | impl Writeable for String { |
126 | #[inline ] |
127 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
128 | sink.write_str(self) |
129 | } |
130 | |
131 | #[inline ] |
132 | fn writeable_length_hint(&self) -> LengthHint { |
133 | LengthHint::exact(self.len()) |
134 | } |
135 | |
136 | #[inline ] |
137 | fn write_to_string(&self) -> Cow<str> { |
138 | Cow::Borrowed(self) |
139 | } |
140 | |
141 | #[inline ] |
142 | fn writeable_cmp_bytes(&self, other: &[u8]) -> core::cmp::Ordering { |
143 | self.as_bytes().cmp(other) |
144 | } |
145 | } |
146 | |
147 | impl Writeable for char { |
148 | #[inline ] |
149 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
150 | sink.write_char(*self) |
151 | } |
152 | |
153 | #[inline ] |
154 | fn writeable_length_hint(&self) -> LengthHint { |
155 | LengthHint::exact(self.len_utf8()) |
156 | } |
157 | |
158 | #[inline ] |
159 | fn write_to_string(&self) -> Cow<str> { |
160 | let mut s: String = String::with_capacity(self.len_utf8()); |
161 | s.push(*self); |
162 | Cow::Owned(s) |
163 | } |
164 | |
165 | #[inline ] |
166 | fn writeable_cmp_bytes(&self, other: &[u8]) -> core::cmp::Ordering { |
167 | self.encode_utf8(&mut [0u8; 4]).as_bytes().cmp(other) |
168 | } |
169 | } |
170 | |
171 | impl<T: Writeable + ?Sized> Writeable for &T { |
172 | #[inline ] |
173 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
174 | (*self).write_to(sink) |
175 | } |
176 | |
177 | #[inline ] |
178 | fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
179 | (*self).write_to_parts(sink) |
180 | } |
181 | |
182 | #[inline ] |
183 | fn writeable_length_hint(&self) -> LengthHint { |
184 | (*self).writeable_length_hint() |
185 | } |
186 | |
187 | #[inline ] |
188 | fn write_to_string(&self) -> Cow<str> { |
189 | (*self).write_to_string() |
190 | } |
191 | |
192 | #[inline ] |
193 | fn writeable_cmp_bytes(&self, other: &[u8]) -> core::cmp::Ordering { |
194 | (*self).writeable_cmp_bytes(other) |
195 | } |
196 | } |
197 | |
198 | macro_rules! impl_write_smart_pointer { |
199 | ($ty:path, T: $extra_bound:path) => { |
200 | impl<'a, T: ?Sized + Writeable + $extra_bound> Writeable for $ty { |
201 | #[inline] |
202 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
203 | core::borrow::Borrow::<T>::borrow(self).write_to(sink) |
204 | } |
205 | #[inline] |
206 | fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
207 | core::borrow::Borrow::<T>::borrow(self).write_to_parts(sink) |
208 | } |
209 | #[inline] |
210 | fn writeable_length_hint(&self) -> LengthHint { |
211 | core::borrow::Borrow::<T>::borrow(self).writeable_length_hint() |
212 | } |
213 | #[inline] |
214 | fn write_to_string(&self) -> Cow<str> { |
215 | core::borrow::Borrow::<T>::borrow(self).write_to_string() |
216 | } |
217 | #[inline] |
218 | fn writeable_cmp_bytes(&self, other: &[u8]) -> core::cmp::Ordering { |
219 | core::borrow::Borrow::<T>::borrow(self).writeable_cmp_bytes(other) |
220 | } |
221 | } |
222 | }; |
223 | ($ty:path) => { |
224 | // Add a harmless duplicate Writeable bound |
225 | impl_write_smart_pointer!($ty, T: Writeable); |
226 | }; |
227 | } |
228 | |
229 | impl_write_smart_pointer!(Cow<'a, T>, T: alloc::borrow::ToOwned); |
230 | impl_write_smart_pointer!(alloc::boxed::Box<T>); |
231 | impl_write_smart_pointer!(alloc::rc::Rc<T>); |
232 | impl_write_smart_pointer!(alloc::sync::Arc<T>); |
233 | |
234 | #[test ] |
235 | fn test_string_impls() { |
236 | fn check_writeable_slice<W: Writeable + core::fmt::Display>(writeables: &[W]) { |
237 | assert_writeable_eq!(&writeables[0], "" ); |
238 | assert_writeable_eq!(&writeables[1], "abc" ); |
239 | assert!(matches!(writeables[0].write_to_string(), Cow::Borrowed(_))); |
240 | assert!(matches!(writeables[1].write_to_string(), Cow::Borrowed(_))); |
241 | } |
242 | |
243 | // test str impl |
244 | let arr: &[&str] = &["" , "abc" ]; |
245 | check_writeable_slice(arr); |
246 | |
247 | // test String impl |
248 | let arr: &[String] = &[String::new(), "abc" .to_owned()]; |
249 | check_writeable_slice(arr); |
250 | |
251 | // test char impl |
252 | let chars = ['a' , 'β' , '你' , '😀' ]; |
253 | for i in 0..chars.len() { |
254 | let s = String::from(chars[i]); |
255 | assert_writeable_eq!(&chars[i], s); |
256 | for j in 0..chars.len() { |
257 | assert_eq!( |
258 | chars[j].writeable_cmp_bytes(s.as_bytes()), |
259 | chars[j].cmp(&chars[i]), |
260 | "{:?} vs {:?}" , |
261 | chars[j], |
262 | chars[i] |
263 | ); |
264 | } |
265 | } |
266 | |
267 | // test Cow impl |
268 | let arr: &[Cow<str>] = &[Cow::Borrowed("" ), Cow::Owned("abc" .to_string())]; |
269 | check_writeable_slice(arr); |
270 | |
271 | // test Box impl |
272 | let arr: &[Box<str>] = &["" .into(), "abc" .into()]; |
273 | check_writeable_slice(arr); |
274 | |
275 | // test Rc impl |
276 | let arr: &[alloc::rc::Rc<str>] = &["" .into(), "abc" .into()]; |
277 | check_writeable_slice(arr); |
278 | |
279 | // test Arc impl |
280 | let arr: &[alloc::sync::Arc<str>] = &["" .into(), "abc" .into()]; |
281 | check_writeable_slice(arr); |
282 | |
283 | // test &T impl |
284 | let arr: &[&String] = &[&String::new(), &"abc" .to_owned()]; |
285 | check_writeable_slice(arr); |
286 | } |
287 | |