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// }