Skip to main content

commonware_codec/types/
range.rs

1//! Codec implementations for range types ([`Range`], [`RangeFrom`], [`RangeTo`],
2//! [`RangeInclusive`], [`RangeToInclusive`], [`RangeFull`]).
3
4use crate::{BufsMut, EncodeSize, Error, FixedSize, Read, Write};
5use bytes::{Buf, BufMut};
6use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
7
8impl<T: Write + PartialOrd> Write for Range<T> {
9    #[inline]
10    fn write(&self, buf: &mut impl BufMut) {
11        assert!(
12            self.start.partial_cmp(&self.end).is_some_and(|o| o.is_le()),
13            "start must be <= end"
14        );
15        self.start.write(buf);
16        self.end.write(buf);
17    }
18
19    #[inline]
20    fn write_bufs(&self, buf: &mut impl BufsMut) {
21        assert!(
22            self.start.partial_cmp(&self.end).is_some_and(|o| o.is_le()),
23            "start must be <= end"
24        );
25        self.start.write_bufs(buf);
26        self.end.write_bufs(buf);
27    }
28}
29
30impl<T: EncodeSize> EncodeSize for Range<T> {
31    #[inline]
32    fn encode_size(&self) -> usize {
33        self.start.encode_size() + self.end.encode_size()
34    }
35
36    #[inline]
37    fn encode_inline_size(&self) -> usize {
38        self.start.encode_inline_size() + self.end.encode_inline_size()
39    }
40}
41
42impl<T: Read + PartialOrd> Read for Range<T> {
43    type Cfg = T::Cfg;
44
45    #[inline]
46    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
47        let start = T::read_cfg(buf, cfg)?;
48        let end = T::read_cfg(buf, cfg)?;
49        if !start.partial_cmp(&end).is_some_and(|o| o.is_le()) {
50            return Err(Error::Invalid("Range", "start must be <= end"));
51        }
52        Ok(start..end)
53    }
54}
55
56impl<T: Write + PartialOrd> Write for RangeInclusive<T> {
57    #[inline]
58    fn write(&self, buf: &mut impl BufMut) {
59        assert!(
60            self.start()
61                .partial_cmp(self.end())
62                .is_some_and(|o| o.is_le()),
63            "start must be <= end"
64        );
65        self.start().write(buf);
66        self.end().write(buf);
67    }
68
69    #[inline]
70    fn write_bufs(&self, buf: &mut impl BufsMut) {
71        assert!(
72            self.start()
73                .partial_cmp(self.end())
74                .is_some_and(|o| o.is_le()),
75            "start must be <= end"
76        );
77        self.start().write_bufs(buf);
78        self.end().write_bufs(buf);
79    }
80}
81
82impl<T: EncodeSize> EncodeSize for RangeInclusive<T> {
83    #[inline]
84    fn encode_size(&self) -> usize {
85        self.start().encode_size() + self.end().encode_size()
86    }
87
88    #[inline]
89    fn encode_inline_size(&self) -> usize {
90        self.start().encode_inline_size() + self.end().encode_inline_size()
91    }
92}
93
94impl<T: Read + PartialOrd> Read for RangeInclusive<T> {
95    type Cfg = T::Cfg;
96
97    #[inline]
98    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
99        let start = T::read_cfg(buf, cfg)?;
100        let end = T::read_cfg(buf, cfg)?;
101        if !start.partial_cmp(&end).is_some_and(|o| o.is_le()) {
102            return Err(Error::Invalid("RangeInclusive", "start must be <= end"));
103        }
104        Ok(start..=end)
105    }
106}
107
108impl<T: Write> Write for RangeFrom<T> {
109    #[inline]
110    fn write(&self, buf: &mut impl BufMut) {
111        self.start.write(buf);
112    }
113
114    #[inline]
115    fn write_bufs(&self, buf: &mut impl BufsMut) {
116        self.start.write_bufs(buf);
117    }
118}
119
120impl<T: EncodeSize> EncodeSize for RangeFrom<T> {
121    #[inline]
122    fn encode_size(&self) -> usize {
123        self.start.encode_size()
124    }
125
126    #[inline]
127    fn encode_inline_size(&self) -> usize {
128        self.start.encode_inline_size()
129    }
130}
131
132impl<T: Read> Read for RangeFrom<T> {
133    type Cfg = T::Cfg;
134
135    #[inline]
136    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
137        let start = T::read_cfg(buf, cfg)?;
138        Ok(start..)
139    }
140}
141
142impl<T: Write> Write for RangeTo<T> {
143    #[inline]
144    fn write(&self, buf: &mut impl BufMut) {
145        self.end.write(buf);
146    }
147
148    #[inline]
149    fn write_bufs(&self, buf: &mut impl BufsMut) {
150        self.end.write_bufs(buf);
151    }
152}
153
154impl<T: EncodeSize> EncodeSize for RangeTo<T> {
155    #[inline]
156    fn encode_size(&self) -> usize {
157        self.end.encode_size()
158    }
159
160    #[inline]
161    fn encode_inline_size(&self) -> usize {
162        self.end.encode_inline_size()
163    }
164}
165
166impl<T: Read> Read for RangeTo<T> {
167    type Cfg = T::Cfg;
168
169    #[inline]
170    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
171        let end = T::read_cfg(buf, cfg)?;
172        Ok(..end)
173    }
174}
175
176impl<T: Write> Write for RangeToInclusive<T> {
177    #[inline]
178    fn write(&self, buf: &mut impl BufMut) {
179        self.end.write(buf);
180    }
181
182    #[inline]
183    fn write_bufs(&self, buf: &mut impl BufsMut) {
184        self.end.write_bufs(buf);
185    }
186}
187
188impl<T: EncodeSize> EncodeSize for RangeToInclusive<T> {
189    #[inline]
190    fn encode_size(&self) -> usize {
191        self.end.encode_size()
192    }
193
194    #[inline]
195    fn encode_inline_size(&self) -> usize {
196        self.end.encode_inline_size()
197    }
198}
199
200impl<T: Read> Read for RangeToInclusive<T> {
201    type Cfg = T::Cfg;
202
203    #[inline]
204    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
205        let end = T::read_cfg(buf, cfg)?;
206        Ok(..=end)
207    }
208}
209
210impl Write for RangeFull {
211    #[inline]
212    fn write(&self, _buf: &mut impl BufMut) {}
213}
214
215impl FixedSize for RangeFull {
216    const SIZE: usize = 0;
217}
218
219impl Read for RangeFull {
220    type Cfg = ();
221
222    #[inline]
223    fn read_cfg(_buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
224        Ok(..)
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use crate::{DecodeExt, Encode, FixedSize};
231    use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
232
233    #[test]
234    fn test_range() {
235        let range: Range<u32> = 10..20;
236        let encoded = range.encode();
237        assert_eq!(encoded.len(), u32::SIZE * 2);
238        let decoded = Range::<u32>::decode(encoded).unwrap();
239        assert_eq!(range, decoded);
240    }
241
242    #[test]
243    fn test_range_inclusive() {
244        let range: RangeInclusive<u32> = 10..=20;
245        let encoded = range.encode();
246        assert_eq!(encoded.len(), u32::SIZE * 2);
247        let decoded = RangeInclusive::<u32>::decode(encoded).unwrap();
248        assert_eq!(range, decoded);
249    }
250
251    #[test]
252    fn test_range_from() {
253        let range: RangeFrom<u32> = 10..;
254        let encoded = range.encode();
255        assert_eq!(encoded.len(), u32::SIZE);
256        let decoded = RangeFrom::<u32>::decode(encoded).unwrap();
257        assert_eq!(range, decoded);
258    }
259
260    #[test]
261    fn test_range_to() {
262        let range: RangeTo<u32> = ..20;
263        let encoded = range.encode();
264        assert_eq!(encoded.len(), u32::SIZE);
265        let decoded = RangeTo::<u32>::decode(encoded).unwrap();
266        assert_eq!(range, decoded);
267    }
268
269    #[test]
270    fn test_range_to_inclusive() {
271        let range: RangeToInclusive<u32> = ..=20;
272        let encoded = range.encode();
273        assert_eq!(encoded.len(), u32::SIZE);
274        let decoded = RangeToInclusive::<u32>::decode(encoded).unwrap();
275        assert_eq!(range, decoded);
276    }
277
278    #[test]
279    fn test_range_full() {
280        let encoded = RangeFull.encode();
281        assert_eq!(encoded.len(), 0);
282        assert_eq!(RangeFull::SIZE, 0);
283        let decoded = RangeFull::decode(encoded).unwrap();
284        assert_eq!(.., decoded);
285    }
286
287    #[test]
288    #[should_panic(expected = "start must be <= end")]
289    fn test_range_encode_invalid() {
290        let range = Range {
291            start: 20u32,
292            end: 10u32,
293        };
294        let _ = range.encode();
295    }
296
297    #[test]
298    fn test_range_decode_invalid() {
299        // Manually encode start=20, end=10 to bypass the write panic
300        let mut buf = Vec::new();
301        buf.extend_from_slice(&20u32.to_be_bytes());
302        buf.extend_from_slice(&10u32.to_be_bytes());
303        assert!(matches!(
304            Range::<u32>::decode(bytes::Bytes::from(buf)),
305            Err(crate::Error::Invalid("Range", "start must be <= end"))
306        ));
307    }
308
309    #[test]
310    #[should_panic(expected = "start must be <= end")]
311    fn test_range_inclusive_encode_invalid() {
312        let range = RangeInclusive::new(20u32, 10u32);
313        let _ = range.encode();
314    }
315
316    #[test]
317    fn test_range_inclusive_decode_invalid() {
318        // Manually encode start=20, end=10 to bypass the write panic
319        let mut buf = Vec::new();
320        buf.extend_from_slice(&20u32.to_be_bytes());
321        buf.extend_from_slice(&10u32.to_be_bytes());
322        assert!(matches!(
323            RangeInclusive::<u32>::decode(bytes::Bytes::from(buf)),
324            Err(crate::Error::Invalid(
325                "RangeInclusive",
326                "start must be <= end"
327            ))
328        ));
329    }
330
331    #[test]
332    fn test_conformity() {
333        assert_eq!(
334            (0x0102u16..0x0304u16).encode(),
335            &[0x01, 0x02, 0x03, 0x04][..]
336        );
337        assert_eq!(
338            (0x0102u16..=0x0304u16).encode(),
339            &[0x01, 0x02, 0x03, 0x04][..]
340        );
341        assert_eq!((0x0102u16..).encode(), &[0x01, 0x02][..]);
342        assert_eq!((..0x0304u16).encode(), &[0x03, 0x04][..]);
343        assert_eq!((..=0x0304u16).encode(), &[0x03, 0x04][..]);
344        assert_eq!((..).encode(), &[][..]);
345    }
346
347    #[cfg(feature = "arbitrary")]
348    mod conformance {
349        use crate::conformance::CodecConformance;
350        use core::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
351
352        commonware_conformance::conformance_tests! {
353            CodecConformance<Range<u32>>,
354            CodecConformance<Range<u64>>,
355            CodecConformance<RangeInclusive<u32>>,
356            CodecConformance<RangeInclusive<u64>>,
357            CodecConformance<RangeFrom<u32>>,
358            CodecConformance<RangeTo<u32>>,
359            CodecConformance<RangeToInclusive<u32>>,
360        }
361    }
362}