Skip to main content

slice_codec/
encoding.rs

1// Copyright (c) ZeroC, Inc.
2
3use crate::buffer::OutputTarget;
4use crate::encode_into::*;
5use crate::encoder::Encoder;
6use crate::{Error, InvalidDataErrorKind, Result, VARINT62_MAX, VARINT62_MIN, VARUINT62_MAX, VARUINT62_MIN};
7
8// We only support 'owned' sequence/dictionary types if the `alloc` crate is available through the `alloc` feature flag.
9// Note that we always support encoding views into these types (which don't require allocating memory).
10#[cfg(feature = "alloc")]
11use alloc::collections::BTreeMap;
12#[cfg(feature = "alloc")]
13use alloc::string::String;
14#[cfg(feature = "alloc")]
15use alloc::vec::Vec;
16
17// We only support `HashMap` if the standard library is available through the `std` feature flag.
18#[cfg(feature = "std")]
19use std::collections::HashMap;
20
21// =============================================================================
22// Fixed-length type implementations
23// =============================================================================
24
25impl EncodeInto for bool {
26    /// Encodes a value of `0` for `false` or a value of `1` for true, on a single byte.
27    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
28        // In memory, bools are guaranteed to be `0` for `false` and `1` for `true`.
29        encoder.write_byte(self as u8)
30    }
31}
32implement_encode_into_on_borrowed_type!(bool);
33
34impl EncodeInto for u8 {
35    /// Writes this byte directly to the buffer, as is.
36    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
37        encoder.write_byte(self)
38    }
39}
40implement_encode_into_on_borrowed_type!(u8);
41
42impl EncodeInto for i8 {
43    /// Writes this `i8` directly to the buffer, treating it as-if it was a `u8`.
44    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
45        // Casting between two integers of the same size (`u8` and `i8`) is no-op in Rust.
46        encoder.write_byte(self as u8)
47    }
48}
49implement_encode_into_on_borrowed_type!(i8);
50
51implement_encode_into_on_numeric_primitive_type! {u16, "Encodes this [`u16`] on 2 bytes (little endian)."}
52implement_encode_into_on_numeric_primitive_type! {i16, "Encodes this [`i16`] on 2 bytes (little endian) in two's complement form."}
53implement_encode_into_on_numeric_primitive_type! {u32, "Encodes this [`u32`] on 4 bytes (little endian)."}
54implement_encode_into_on_numeric_primitive_type! {i32, "Encodes this [`i32`] on 4 bytes (little endian) in two's complement form."}
55implement_encode_into_on_numeric_primitive_type! {u64, "Encodes this [`u64`] on 8 bytes (little endian)."}
56implement_encode_into_on_numeric_primitive_type! {i64, "Encodes this [`i64`] on 8 bytes (little endian) in two's complement form."}
57implement_encode_into_on_numeric_primitive_type! {f32, "Encodes this [`f32`] on 4 bytes (little endian) using the \"binary32\" representation defined in IEEE 754-2008."}
58implement_encode_into_on_numeric_primitive_type! {f64, "Encodes this [`f64`] on 8 bytes (little endian) using the \"binary64\" representation defined in IEEE 754-2008."}
59
60// =============================================================================
61// Variable-length integer type implementations
62// =============================================================================
63
64/// TODO
65fn varint_range_error(value: impl Into<i128>) -> Error {
66    let error = InvalidDataErrorKind::OutOfRange {
67        value: value.into(),
68        min: VARINT62_MIN as i128,
69        max: VARINT62_MAX as i128,
70        typename: "varint62",
71    };
72    error.into()
73}
74
75/// TODO
76fn varuint_range_error(value: impl Into<i128>) -> Error {
77    let error = InvalidDataErrorKind::OutOfRange {
78        value: value.into(),
79        min: VARUINT62_MIN as i128,
80        max: VARUINT62_MAX as i128,
81        typename: "varuint62",
82    };
83    error.into()
84}
85
86impl<O: OutputTarget> Encoder<O> {
87    /// Encodes a signed integer on between 1 and 8 bytes in the variable length '[varint]' format.
88    ///
89    /// [varint]: https://docs.icerpc.dev/slice/language-guide/primitive-types#variable-size-integral-types
90    #[rustfmt::skip] // To keep the arms of `match required_bits` aligned for readability.
91    pub fn encode_varint(&mut self, value: impl Into<i64>) -> Result<()> {
92        let value: i64 = value.into();
93
94        // See: https://docs.icerpc.dev/slice/encoding/primitive-types#variable-size-integral-types.
95
96        // Compute how many bits are required to encode this value.
97        let mut required_bits = i64::BITS - match value.is_negative() {
98            // If the value is non-negative, we can ignore any leading `0` bits.
99            false => value.leading_zeros(),
100            // If the value is negative, we can ignore any leading `1` bits.
101            true => value.leading_ones(),
102        };
103        required_bits += 1; // Add '1' to account for the sign bit.
104        // We have to shift the value up by 2 bits to make room for the size prefix.
105        let shifted_value: i64 = value << 2;
106
107        match required_bits {
108            0..=6   => self.encode(shifted_value as i8),
109            7..=14  => self.encode(shifted_value as i16 | 0b01),
110            15..=30 => self.encode(shifted_value as i32 | 0b10),
111            31..=62 => self.encode(shifted_value | 0b11),
112            63.. => Err(varint_range_error(value)),
113        }
114    }
115
116    /// Encodes an unsigned integer on between 1 and 8 bytes in the variable length '[varuint]' format.
117    ///
118    /// [varuint]: https://docs.icerpc.dev/slice/language-guide/primitive-types#variable-size-integral-types
119    #[rustfmt::skip] // To keep the arms of `match required_bits` aligned for readability.
120    pub fn encode_varuint(&mut self, value: impl Into<u64>) -> Result<()> {
121        let value: u64 = value.into();
122
123        // See: https://docs.icerpc.dev/slice/encoding/primitive-types#variable-size-integral-types.
124
125        // Compute how many bits are required to encode this value.
126        let required_bits = u64::BITS - value.leading_zeros();
127        // We have to shift the value up by 2 bits to make room for the size prefix.
128        let shifted_value: u64 = value << 2;
129
130        match required_bits {
131            0..=6   => self.encode(shifted_value as u8),
132            7..=14  => self.encode(shifted_value as u16 | 0b01),
133            15..=30 => self.encode(shifted_value as u32 | 0b10),
134            31..=62 => self.encode(shifted_value | 0b11),
135            63.. => Err(varuint_range_error(value)),
136        }
137    }
138
139    // An alias for `[encode_varuint]` to increase readability.
140    pub fn encode_size(&mut self, value: usize) -> Result<()> {
141        let size = u64::try_from(value)?;
142        self.encode_varuint(size)
143    }
144}
145
146// =============================================================================
147// Sequence type implementations
148// =============================================================================
149
150/// TODO
151impl EncodeInto for &str {
152    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
153        encoder.encode_size(self.len())?;
154        encoder.write_bytes_exact(self.as_bytes())
155    }
156}
157
158#[cfg(feature = "alloc")]
159impl EncodeInto for &String {
160    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
161        self.as_str().encode_into(encoder)
162    }
163}
164
165/// TODO
166impl<'a, T> EncodeInto for &'a [T]
167where
168    &'a T: EncodeInto,
169{
170    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
171        encoder.encode_size(self.len())?;
172        for element in self {
173            encoder.encode(element)?;
174        }
175        Ok(())
176    }
177}
178
179#[cfg(feature = "alloc")]
180impl<'a, T> EncodeInto for &'a Vec<T>
181where
182    &'a T: EncodeInto,
183{
184    fn encode_into(self, encoder: &mut Encoder<impl OutputTarget>) -> Result<()> {
185        self.as_slice().encode_into(encoder)
186    }
187}
188
189// =============================================================================
190// Dictionary type implementations
191// =============================================================================
192
193#[cfg(feature = "alloc")]
194impl_encode_into_on_dictionary_type!(BTreeMap, "TODO");
195
196#[cfg(feature = "std")]
197impl_encode_into_on_dictionary_type!(HashMap, "TODO");
198
199// TODO add support for optional sequences and dictionaries