Skip to main content

zenoh_codec/core/
zint.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14use zenoh_buffers::{
15    reader::{DidntRead, Reader},
16    writer::{DidntWrite, Writer},
17};
18
19use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Bounded};
20
21const VLE_LEN_MAX: usize = vle_len(u64::MAX);
22
23const fn vle_len(x: u64) -> usize {
24    const B1: u64 = u64::MAX << 7;
25    const B2: u64 = u64::MAX << (7 * 2);
26    const B3: u64 = u64::MAX << (7 * 3);
27    const B4: u64 = u64::MAX << (7 * 4);
28    const B5: u64 = u64::MAX << (7 * 5);
29    const B6: u64 = u64::MAX << (7 * 6);
30    const B7: u64 = u64::MAX << (7 * 7);
31    const B8: u64 = u64::MAX << (7 * 8);
32
33    if (x & B1) == 0 {
34        1
35    } else if (x & B2) == 0 {
36        2
37    } else if (x & B3) == 0 {
38        3
39    } else if (x & B4) == 0 {
40        4
41    } else if (x & B5) == 0 {
42        5
43    } else if (x & B6) == 0 {
44        6
45    } else if (x & B7) == 0 {
46        7
47    } else if (x & B8) == 0 {
48        8
49    } else {
50        9
51    }
52}
53
54impl LCodec<u64> for Zenoh080 {
55    fn w_len(self, x: u64) -> usize {
56        vle_len(x)
57    }
58}
59
60impl LCodec<usize> for Zenoh080 {
61    fn w_len(self, x: usize) -> usize {
62        self.w_len(x as u64)
63    }
64}
65
66impl LCodec<u32> for Zenoh080 {
67    fn w_len(self, x: u32) -> usize {
68        self.w_len(x as u64)
69    }
70}
71
72impl LCodec<u16> for Zenoh080 {
73    fn w_len(self, x: u16) -> usize {
74        self.w_len(x as u64)
75    }
76}
77
78impl LCodec<u8> for Zenoh080 {
79    fn w_len(self, _: u8) -> usize {
80        1
81    }
82}
83
84// u8
85impl<W> WCodec<u8, &mut W> for Zenoh080
86where
87    W: Writer,
88{
89    type Output = Result<(), DidntWrite>;
90
91    #[inline(always)]
92    fn write(self, writer: &mut W, x: u8) -> Self::Output {
93        writer.write_u8(x)
94    }
95}
96
97impl<R> RCodec<u8, &mut R> for Zenoh080
98where
99    R: Reader,
100{
101    type Error = DidntRead;
102
103    #[inline(always)]
104    fn read(self, reader: &mut R) -> Result<u8, Self::Error> {
105        reader.read_u8()
106    }
107}
108
109// u64
110impl<W> WCodec<u64, &mut W> for Zenoh080
111where
112    W: Writer,
113{
114    type Output = Result<(), DidntWrite>;
115
116    fn write(self, writer: &mut W, mut x: u64) -> Self::Output {
117        let write = move |buffer: &mut [u8]| {
118            let mut len = 0;
119            while (x & !0x7f_u64) != 0 {
120                // SAFETY: buffer is guaranteed to be VLE_LEN long where VLE_LEN is
121                //         the maximum number of bytes a VLE can take once encoded.
122                //         I.e.: x is shifted 7 bits to the right every iteration,
123                //         the loop is at most VLE_LEN iterations.
124                unsafe {
125                    *buffer.get_unchecked_mut(len) = (x as u8) | 0x80_u8;
126                }
127                len += 1;
128                x >>= 7;
129            }
130            // In case len == VLE_LEN then all the bits have already been written in the latest iteration.
131            // Else we haven't written all the necessary bytes yet.
132            if len != VLE_LEN_MAX {
133                // SAFETY: buffer is guaranteed to be VLE_LEN long where VLE_LEN is
134                //         the maximum number of bytes a VLE can take once encoded.
135                //         I.e.: x is shifted 7 bits to the right every iteration,
136                //         the loop is at most VLE_LEN iterations.
137                unsafe {
138                    *buffer.get_unchecked_mut(len) = x as u8;
139                }
140                len += 1;
141            }
142            // The number of written bytes
143            len
144        };
145        // SAFETY: write algorithm guarantees than returned length is lesser than or equal to
146        // `VLE_LEN_MAX`.
147        unsafe { writer.with_slot(VLE_LEN_MAX, write)? };
148        Ok(())
149    }
150}
151
152impl<R> RCodec<u64, &mut R> for Zenoh080
153where
154    R: Reader,
155{
156    type Error = DidntRead;
157
158    fn read(self, reader: &mut R) -> Result<u64, Self::Error> {
159        let mut b = reader.read_u8()?;
160
161        let mut v = 0;
162        let mut i = 0;
163        // 7 * VLE_LEN is beyond the maximum number of shift bits
164        while (b & 0x80_u8) != 0 && i != 7 * (VLE_LEN_MAX - 1) {
165            v |= ((b & 0x7f_u8) as u64) << i;
166            b = reader.read_u8()?;
167            i += 7;
168        }
169        v |= (b as u64) << i;
170        Ok(v)
171    }
172}
173
174// Derive impls
175macro_rules! uint_impl {
176    ($uint:ty) => {
177        impl<W> WCodec<$uint, &mut W> for Zenoh080
178        where
179            W: Writer,
180        {
181            type Output = Result<(), DidntWrite>;
182
183            fn write(self, writer: &mut W, x: $uint) -> Self::Output {
184                self.write(writer, x as u64)
185            }
186        }
187
188        impl<R> RCodec<$uint, &mut R> for Zenoh080
189        where
190            R: Reader,
191        {
192            type Error = DidntRead;
193
194            fn read(self, reader: &mut R) -> Result<$uint, Self::Error> {
195                let x: u64 = self.read(reader)?;
196                Ok(x as $uint)
197            }
198        }
199    };
200}
201
202uint_impl!(u16);
203uint_impl!(u32);
204uint_impl!(usize);
205
206macro_rules! uint_ref_impl {
207    ($uint:ty) => {
208        impl<W> WCodec<&$uint, &mut W> for Zenoh080
209        where
210            W: Writer,
211        {
212            type Output = Result<(), DidntWrite>;
213
214            fn write(self, writer: &mut W, x: &$uint) -> Self::Output {
215                self.write(writer, *x)
216            }
217        }
218    };
219}
220
221uint_ref_impl!(u8);
222uint_ref_impl!(u16);
223uint_ref_impl!(u32);
224uint_ref_impl!(u64);
225uint_ref_impl!(usize);
226
227// Encode unsigned integer and verify that the size boundaries are respected
228macro_rules! zint_impl_codec {
229    ($zint:ty, $bound:ty) => {
230        impl<W> WCodec<$zint, &mut W> for Zenoh080Bounded<$bound>
231        where
232            W: Writer,
233        {
234            type Output = Result<(), DidntWrite>;
235
236            fn write(self, writer: &mut W, x: $zint) -> Self::Output {
237                if (x as u64 & !(<$bound>::MAX as u64)) != 0 {
238                    return Err(DidntWrite);
239                }
240                Zenoh080.write(writer, x as u64)
241            }
242        }
243
244        impl<R> RCodec<$zint, &mut R> for Zenoh080Bounded<$bound>
245        where
246            R: Reader,
247        {
248            type Error = DidntRead;
249
250            #[inline(always)]
251            fn read(self, reader: &mut R) -> Result<$zint, Self::Error> {
252                let x: u64 = Zenoh080.read(reader)?;
253                if (x & !(<$bound>::MAX as u64)) != 0 {
254                    return Err(DidntRead);
255                }
256                Ok(x as $zint)
257            }
258        }
259    };
260}
261macro_rules! zint_impl {
262    ($zint:ty) => {
263        zint_impl_codec!($zint, u8);
264        zint_impl_codec!($zint, u16);
265        zint_impl_codec!($zint, u32);
266        zint_impl_codec!($zint, u64);
267        zint_impl_codec!($zint, usize);
268    };
269}
270
271zint_impl!(u8);
272zint_impl!(u16);
273zint_impl!(u32);
274zint_impl!(u64);
275zint_impl!(usize);
276
277// const MAX_LEN: usize = 9;
278// const VLE_THR: u64 = 0xf8; // 248
279// impl<W> WCodec<u64, &mut W> for Zenoh080
280// where
281//     W: Writer,
282// {
283//     type Output = Result<(), DidntWrite>;
284//     fn write(self, writer: &mut W, mut x: u64) -> Self::Output {
285//         writer.with_slot(MAX_LEN, move |into| {
286//             if x < VLE_THR {
287//                 into[0] = x as u8;
288//                 return 1;
289//             }
290//             x -= VLE_THR - 1;
291//             // SAFETY
292//             // The `if x < VLE_THR` check at the beginning followed by `x -= VLE_THR - 1`
293//             // guarantees at this point that `x` is never `0`. Since `x` is 64bit,
294//             // then `n` is guaranteed to have a value between 1 and 8, both inclusives.
295//             // `into` is guaranteed to be exactly 9 bytes long. Therefore, copying at most
296//             // 8 bytes with a pointer offset of 1 is actually safe.
297//             let n = 8 - (x.leading_zeros() / 8) as usize;
298//             unsafe {
299//                 core::ptr::copy_nonoverlapping(
300//                     x.to_le_bytes().as_ptr(),
301//                     into.as_mut_ptr().offset(1),
302//                     n,
303//                 )
304//             }
305//             into[0] = VLE_THR as u8 | (n - 1) as u8;
306//             1 + n
307//         })
308//     }
309// }
310
311// impl<R> RCodec<u64, &mut R> for Zenoh080
312// where
313//     R: Reader,
314// {
315//     type Error = DidntRead;
316//     fn read(self, reader: &mut R) -> Result<u64, Self::Error> {
317//         let b = reader.read_u8()?;
318//         if b < (VLE_THR as u8) {
319//             return Ok(b as u64);
320//         }
321//         let n = (1 + (b & !VLE_THR as u8)) as usize;
322//         let mut u64: [u8; 8] = 0u64.to_le_bytes();
323//         reader.read_exact(&mut u64[0..n])?;
324//         let u64 = u64::from_le_bytes(u64);
325//         Ok(u64.saturating_add(VLE_THR - 1))
326//     }
327// }
328
329// mod tests {
330//     #[test]
331//     fn u64_overhead() {
332//         use crate::{WCodec, Zenoh080};
333//         use zenoh_buffers::{
334//             reader::{HasReader, Reader},
335//             writer::HasWriter,
336//         };
337
338//         fn overhead(x: u64) -> usize {
339//             let codec = Zenoh080::new();
340//             let mut b = vec![];
341//             let mut w = b.writer();
342//             codec.write(&mut w, x).unwrap();
343//             let r = b.reader().remaining();
344//             println!("{} {}", x, r);
345//             r
346//         }
347
348//         assert_eq!(overhead(247), 1);
349//         assert_eq!(overhead(248), 2);
350//         assert_eq!(overhead(502), 2);
351//         assert_eq!(overhead(503), 3);
352//         assert_eq!(overhead(65_782), 3);
353//         assert_eq!(overhead(65_783), 4);
354//         assert_eq!(overhead(16_777_462), 4);
355//         assert_eq!(overhead(16_777_463), 5);
356//         assert_eq!(overhead(4_294_967_542), 5);
357//         assert_eq!(overhead(4_294_967_543), 6);
358//         assert_eq!(overhead(1_099_511_628_022), 6);
359//         assert_eq!(overhead(1_099_511_628_023), 7);
360//         assert_eq!(overhead(281_474_976_710_902), 7);
361//         assert_eq!(overhead(281_474_976_710_903), 8);
362//         assert_eq!(overhead(72_057_594_037_928_182), 8);
363//         assert_eq!(overhead(72_057_594_037_928_183), 9);
364//         assert_eq!(overhead(u64::MAX), 9);
365//     }
366// }
367
368// macro_rules! non_zero_array {
369//     ($($i: expr,)*) => {
370//         [$(match NonZeroU8::new($i) {Some(x) => x, None => panic!("Attempted to place 0 in an array of non-zeros literal")}),*]
371//     };
372// }
373
374// impl Zenoh080 {
375//     pub const fn preview_length(&self, x: u64) -> NonZeroU8 {
376//         let x = match NonZeroU64::new(x) {
377//             Some(x) => x,
378//             None => {
379//                 return unsafe { NonZeroU8::new_unchecked(1) };
380//             }
381//         };
382//         let needed_bits = u64::BITS - x.leading_zeros();
383//         const LENGTH: [NonZeroU8; 65] = non_zero_array![
384//             1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
385//             5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9,
386//             9, 9, 9, 9, 9, 9, 9,
387//         ];
388//         LENGTH[needed_bits as usize]
389//     }
390// }
391
392// impl<W> WCodec<u64, &mut W> for Zenoh080
393// where
394//     W: Writer,
395// {
396//     type Output = Result<(), DidntWrite>;
397
398//     fn write(self, writer: &mut W, x: u64) -> Self::Output {
399//         const VLE_LEN: usize = 9;
400//         const VLE_MASK: [u8; VLE_LEN] = [
401//             0b11111111, // This is padding to avoid needless subtractions on index access
402//             0, 0b00000001, 0b00000011, 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111,
403//         ];
404//         const VLE_SHIFT: [u8; 65] = [
405//             1, // This is padding to avoid needless subtractions on index access
406//             1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5,
407//             5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 0, 0,
408//             0, 0, 0, 0, 0, 0,
409//         ];
410
411//         writer.with_slot(VLE_LEN, move |buffer| {
412//             // since leading_zeros will jump conditionally on 0 anyway (`asm {bsr 0}` is UB), might as well jump to return
413//             let x = match NonZeroU64::new(x) {
414//                 Some(x) => x,
415//                 None => {
416//                     buffer[0] = 0;
417//                     return 1;
418//                 }
419//             };
420
421//             let needed_bits = u64::BITS - x.leading_zeros();
422//             let payload_size = VLE_SHIFT[needed_bits as usize];
423//             let shift_payload = payload_size == 0;
424//             let mut x: u64 = x.into();
425//             x <<= payload_size;
426//             let serialized = x.to_le_bytes();
427
428//             unsafe {
429//                 ptr::copy_nonoverlapping(
430//                     serialized.as_ptr(),
431//                     buffer.as_mut_ptr().offset(shift_payload as isize),
432//                     u64::BITS as usize / 8,
433//                 )
434//             };
435
436//             let needed_bytes = payload_size as usize;
437//             buffer[0] |= VLE_MASK[needed_bytes];
438//             if shift_payload {
439//                 VLE_LEN
440//             } else {
441//                 needed_bytes
442//             }
443//         })?;
444
445//         Ok(())
446//     }
447// }
448
449// impl<W> WCodec<&u64, &mut W> for Zenoh080
450// where
451//     W: Writer,
452// {
453//     type Output = Result<(), DidntWrite>;
454
455//     fn write(self, writer: &mut W, x: &u64) -> Self::Output {
456//         self.write(writer, *x)
457//     }
458// }
459
460// impl<R> RCodec<u64, &mut R> for Zenoh080
461// where
462//     R: Reader,
463// {
464//     type Error = DidntRead;
465
466//     fn read(self, reader: &mut R) -> Result<u64, Self::Error> {
467//         let mut buffer = [0; 8];
468//         buffer[0] = reader.read_u8()?;
469
470//         // GCC: `__builtin_ctz(~buffer[0])`, clang: `__tzcnt_u64(~buffer[0])`
471//         let byte_count = (buffer[0].trailing_ones()) as usize;
472//         if byte_count == 0 {
473//             return Ok(u64::from_le_bytes(buffer) >> 1);
474//         }
475
476//         let shift_payload = (byte_count == 8) as usize; // branches are evil
477//         let shift_payload_multiplier = 1 - shift_payload;
478
479//         let len = byte_count + shift_payload_multiplier;
480//         reader.read_exact(&mut buffer[shift_payload_multiplier..len])?;
481
482//         // the shift also removes the mask
483//         Ok(u64::from_le_bytes(buffer) >> ((byte_count + 1) * shift_payload_multiplier) as u32)
484//     }
485// }
486
487// // usize
488// impl<W> WCodec<usize, &mut W> for Zenoh080
489// where
490//     W: Writer,
491// {
492//     type Output = Result<(), DidntWrite>;
493
494//     fn write(self, writer: &mut W, x: usize) -> Self::Output {
495//         let x: u64 = x.try_into().map_err(|_| DidntWrite)?;
496//         self.write(writer, x)
497//     }
498// }
499
500// impl<R> RCodec<usize, &mut R> for Zenoh080
501// where
502//     R: Reader,
503// {
504//     type Error = DidntRead;
505
506//     fn read(self, reader: &mut R) -> Result<usize, Self::Error> {
507//         let x: u64 = <Self as RCodec<u64, &mut R>>::read(self, reader)?;
508//         x.try_into().map_err(|_| DidntRead)
509//     }
510// }
511
512// #[cfg(test)]
513// mod test {
514//     #[test]
515//     fn u64_fuzz() {
516//         use crate::*;
517//         use rand::Rng;
518//         use zenoh_buffers::{reader::HasReader, writer::HasWriter};
519
520//         const NUM: usize = 1_000;
521//         const LIMIT: [u64; 4] = [u8::MAX as u64, u16::MAX as u64, u32::MAX as u64, u64::MAX];
522
523//         let codec = Zenoh080::new();
524//         let mut rng = rand::thread_rng();
525
526//         for l in LIMIT.iter() {
527//             let mut values = Vec::with_capacity(NUM);
528//             let mut buffer = vec![];
529
530//             let mut writer = buffer.writer();
531
532//             for _ in 0..NUM {
533//                 let x: u64 = rng.gen_range(0..=*l);
534//                 values.push(x);
535//                 codec.write(&mut writer, x).unwrap();
536//             }
537
538//             let mut reader = buffer.reader();
539
540//             for x in values.drain(..).take(NUM) {
541//                 let y: u64 = codec.read(&mut reader).unwrap();
542//                 println!("{x} {y}");
543//                 assert_eq!(x, y);
544//             }
545
546//             assert!(reader.is_empty());
547//         }
548//     }
549// }