ordered_varint/
signed.rs

1use std::io::{Read, Write};
2use std::num::TryFromIntError;
3
4use crate::Variable;
5
6/// A signed integer value.
7///
8/// This type encodes values in the range `-2.pow(123)..2.pow(123)` by using the
9/// first 5 bits to denote a signed byte `length`. This length ranges from
10/// `-15..=15`. The number of bytes read is always absolute, but the sign of the
11/// length is used to determine the overall sign of the encoded value. The
12/// remaining 3 bits of the first byte and any additional bytes are then
13/// used to store the integer in big-endian encoding.
14#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
15pub struct Signed(i128);
16
17impl Signed {
18    pub(crate) fn encode_be_bytes<W: Write, const N: usize>(
19        mut value: [u8; N],
20        mut output: W,
21    ) -> std::io::Result<usize> {
22        let check_bits = if N == 16 {
23            // We reserve 5 bits for a signed 4 bit number, ranging from -16..=15.
24            let reserved = value[0] >> 3;
25            match reserved {
26                0 => 0,
27                0b11111 => 0xFF,
28                _ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
29            }
30        } else if value[0] >> 7 == 0 {
31            // positive
32            0
33        } else {
34            // negative
35            0xff
36        };
37
38        let (total_length, extra_bytes) = value
39            .iter()
40            .enumerate()
41            .find_map(|(index, &byte)| {
42                if byte == check_bits {
43                    None
44                } else {
45                    let extra_bytes = N - 1 - index;
46                    if byte >> 3 == check_bits >> 3 {
47                        Some((extra_bytes + 1, extra_bytes))
48                    } else {
49                        Some((extra_bytes + 2, extra_bytes + 1))
50                    }
51                }
52            })
53            .unwrap_or((0, 0));
54        let total_length = total_length.max(1);
55
56        let length_header = if check_bits == 0 {
57            extra_bytes + 2_usize.pow(4)
58        } else {
59            2_usize.pow(4) - extra_bytes - 1
60        };
61
62        let encoded_length_header = (length_header as u8) << 3;
63        if total_length > N {
64            // We can't fit the length in the buffer.
65            output.write_all(&[encoded_length_header | (check_bits >> 5)])?;
66            output.write_all(&value)?;
67        } else {
68            // Clear the top bits to prepare for the header
69            value[N - total_length] &= 0b111;
70            // Set the length bits
71            value[N - total_length] |= encoded_length_header;
72
73            output.write_all(&value[N - total_length..])?;
74        }
75
76        Ok(total_length)
77    }
78
79    pub(crate) fn decode_variable_bytes<R: Read, const N: usize>(
80        mut input: R,
81    ) -> std::io::Result<[u8; N]> {
82        let mut buffer = [0_u8; N];
83        input.read_exact(&mut buffer[0..1])?;
84        let first_byte = buffer[0];
85        let encoded_length = first_byte as usize >> 3;
86        let (negative, length) = if encoded_length >= 2_usize.pow(4) {
87            (false, encoded_length - 2_usize.pow(4))
88        } else {
89            (true, 2_usize.pow(4) - (encoded_length + 1))
90        };
91        if length > N {
92            return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
93        }
94
95        input.read_exact(&mut buffer[N - length..])?;
96
97        match N - length {
98            0 => {
99                // We overwrote our first byte, but the first byte has some 3
100                // bits of data we need to preserve.
101                let mut first_bits = first_byte & 0b111;
102                if negative {
103                    first_bits ^= 0b111;
104                }
105                buffer[0] |= first_bits << 5;
106            }
107            1 => {
108                // Clear the top 3 bits of the top byte, and negate if needed.
109                buffer[0] &= 0b111;
110                if negative {
111                    buffer[0] ^= 0b1111_1000;
112                }
113            }
114            _ => {
115                buffer[N - 1 - length] |= first_byte & 0b111;
116                if negative {
117                    buffer[N - 1 - length] ^= 0b1111_1000;
118                }
119                buffer[0] = 0;
120            }
121        }
122
123        if negative && N > 1 {
124            let bytes_to_negate = N - length;
125            // We know we can skip updating the last byte that contained data.
126            if bytes_to_negate > 1 {
127                for byte in &mut buffer[0..bytes_to_negate - 1] {
128                    *byte ^= 0xFF;
129                }
130            }
131        }
132
133        Ok(buffer)
134    }
135}
136
137impl Variable for Signed {
138    fn encode_variable<W: Write>(&self, output: W) -> std::io::Result<usize> {
139        Self::encode_be_bytes(self.0.to_be_bytes(), output)
140    }
141
142    fn decode_variable<R: Read>(mut input: R) -> std::io::Result<Self> {
143        let mut buffer = [0_u8; 16];
144        input.read_exact(&mut buffer[0..1])?;
145
146        let encoded_length = buffer[0] as usize >> 3;
147        let (negative, length) = if encoded_length >= 2_usize.pow(4) {
148            (false, encoded_length - 2_usize.pow(4))
149        } else {
150            (true, 2_usize.pow(4) - (encoded_length + 1))
151        };
152
153        input.read_exact(&mut buffer[16 - length..])?;
154
155        if length < 15 {
156            buffer[15 - length] |= buffer[0] & 0b111;
157            if negative {
158                buffer[15 - length] ^= 0b1111_1000;
159            }
160            buffer[0] = 0;
161        } else {
162            buffer[0] &= 0b111;
163            if negative {
164                buffer[0] ^= 0b1111_1000;
165            }
166        }
167
168        if negative {
169            for byte in &mut buffer[0..15 - length] {
170                *byte ^= 0xFF;
171            }
172        }
173
174        Ok(Self(i128::from_be_bytes(buffer)))
175    }
176}
177
178macro_rules! impl_primitive_from_varint {
179    ($ty:ty) => {
180        impl TryFrom<Signed> for $ty {
181            type Error = TryFromIntError;
182
183            fn try_from(value: Signed) -> Result<Self, Self::Error> {
184                value.0.try_into()
185            }
186        }
187    };
188}
189
190macro_rules! impl_varint_from_primitive {
191    ($ty:ty, $dest:ty) => {
192        impl From<$ty> for Signed {
193            fn from(value: $ty) -> Self {
194                Self(<$dest>::from(value))
195            }
196        }
197    };
198}
199
200impl_varint_from_primitive!(i8, i128);
201impl_varint_from_primitive!(i16, i128);
202impl_varint_from_primitive!(i32, i128);
203impl_varint_from_primitive!(i64, i128);
204impl_varint_from_primitive!(i128, i128);
205
206impl_primitive_from_varint!(i8);
207impl_primitive_from_varint!(i16);
208impl_primitive_from_varint!(i32);
209impl_primitive_from_varint!(i64);
210impl_primitive_from_varint!(isize);
211
212impl From<Signed> for i128 {
213    fn from(value: Signed) -> Self {
214        value.0
215    }
216}
217
218impl From<isize> for Signed {
219    fn from(value: isize) -> Self {
220        Self(value as i128)
221    }
222}