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