/rust/registry/src/index.crates.io-1949cf8c6b5b557f/bincode-2.0.1/src/config.rs
Line | Count | Source |
1 | | //! The config module is used to change the behavior of bincode's encoding and decoding logic. |
2 | | //! |
3 | | //! *Important* make sure you use the same config for encoding and decoding, or else bincode will not work properly. |
4 | | //! |
5 | | //! To use a config, first create a type of [Configuration]. This type will implement trait [Config] for use with bincode. |
6 | | //! |
7 | | //! ``` |
8 | | //! let config = bincode::config::standard() |
9 | | //! // pick one of: |
10 | | //! .with_big_endian() |
11 | | //! .with_little_endian() |
12 | | //! // pick one of: |
13 | | //! .with_variable_int_encoding() |
14 | | //! .with_fixed_int_encoding(); |
15 | | //! ``` |
16 | | //! |
17 | | //! See [Configuration] for more information on the configuration options. |
18 | | |
19 | | pub(crate) use self::internal::*; |
20 | | use core::marker::PhantomData; |
21 | | |
22 | | /// The Configuration struct is used to build bincode configurations. The [Config] trait is implemented |
23 | | /// by this struct when a valid configuration has been constructed. |
24 | | /// |
25 | | /// The following methods are mutually exclusive and will overwrite each other. The last call to one of these methods determines the behavior of the configuration: |
26 | | /// |
27 | | /// - [with_little_endian] and [with_big_endian] |
28 | | /// - [with_fixed_int_encoding] and [with_variable_int_encoding] |
29 | | /// |
30 | | /// |
31 | | /// [with_little_endian]: #method.with_little_endian |
32 | | /// [with_big_endian]: #method.with_big_endian |
33 | | /// [with_fixed_int_encoding]: #method.with_fixed_int_encoding |
34 | | /// [with_variable_int_encoding]: #method.with_variable_int_encoding |
35 | | #[derive(Copy, Clone, Debug)] |
36 | | pub struct Configuration<E = LittleEndian, I = Varint, L = NoLimit> { |
37 | | _e: PhantomData<E>, |
38 | | _i: PhantomData<I>, |
39 | | _l: PhantomData<L>, |
40 | | } |
41 | | |
42 | | // When adding more features to configuration, follow these steps: |
43 | | // - Create 2 or more structs that can be used as a type (e.g. Limit and NoLimit) |
44 | | // - Add an `Internal...Config` to the `internal` module |
45 | | // - Make sure `Config` and `impl<T> Config for T` extend from this new trait |
46 | | // - Add a generic to `Configuration` |
47 | | // - Add this generic to `impl<...> Default for Configuration<...>` |
48 | | // - Add this generic to `const fn generate<...>()` |
49 | | // - Add this generic to _every_ function in `Configuration` |
50 | | // - Add your new methods |
51 | | |
52 | | /// The default config for bincode 2.0. By default this will be: |
53 | | /// - Little endian |
54 | | /// - Variable int encoding |
55 | 0 | pub const fn standard() -> Configuration { |
56 | 0 | generate() |
57 | 0 | } |
58 | | |
59 | | /// Creates the "legacy" default config. This is the default config that was present in bincode 1.0 |
60 | | /// - Little endian |
61 | | /// - Fixed int length encoding |
62 | 0 | pub const fn legacy() -> Configuration<LittleEndian, Fixint, NoLimit> { |
63 | 0 | generate() |
64 | 0 | } |
65 | | |
66 | | impl<E, I, L> Default for Configuration<E, I, L> { |
67 | 0 | fn default() -> Self { |
68 | 0 | generate() |
69 | 0 | } |
70 | | } |
71 | | |
72 | 0 | const fn generate<E, I, L>() -> Configuration<E, I, L> { |
73 | 0 | Configuration { |
74 | 0 | _e: PhantomData, |
75 | 0 | _i: PhantomData, |
76 | 0 | _l: PhantomData, |
77 | 0 | } |
78 | 0 | } Unexecuted instantiation: bincode::config::generate::<bincode::config::LittleEndian, bincode::config::Fixint, bincode::config::NoLimit> Unexecuted instantiation: bincode::config::generate::<bincode::config::LittleEndian, bincode::config::Varint, bincode::config::NoLimit> |
79 | | |
80 | | impl<E, I, L> Configuration<E, I, L> { |
81 | | /// Makes bincode encode all integer types in big endian. |
82 | 0 | pub const fn with_big_endian(self) -> Configuration<BigEndian, I, L> { |
83 | 0 | generate() |
84 | 0 | } |
85 | | |
86 | | /// Makes bincode encode all integer types in little endian. |
87 | 0 | pub const fn with_little_endian(self) -> Configuration<LittleEndian, I, L> { |
88 | 0 | generate() |
89 | 0 | } |
90 | | |
91 | | /// Makes bincode encode all integer types with a variable integer encoding. |
92 | | /// |
93 | | /// Encoding an unsigned integer v (of any type excepting u8) works as follows: |
94 | | /// |
95 | | /// 1. If `u < 251`, encode it as a single byte with that value. |
96 | | /// 2. If `251 <= u < 2**16`, encode it as a literal byte 251, followed by a u16 with value `u`. |
97 | | /// 3. If `2**16 <= u < 2**32`, encode it as a literal byte 252, followed by a u32 with value `u`. |
98 | | /// 4. If `2**32 <= u < 2**64`, encode it as a literal byte 253, followed by a u64 with value `u`. |
99 | | /// 5. If `2**64 <= u < 2**128`, encode it as a literal byte 254, followed by a u128 with value `u`. |
100 | | /// |
101 | | /// Then, for signed integers, we first convert to unsigned using the zigzag algorithm, |
102 | | /// and then encode them as we do for unsigned integers generally. The reason we use this |
103 | | /// algorithm is that it encodes those values which are close to zero in less bytes; the |
104 | | /// obvious algorithm, where we encode the cast values, gives a very large encoding for all |
105 | | /// negative values. |
106 | | /// |
107 | | /// The zigzag algorithm is defined as follows: |
108 | | /// |
109 | | /// ```rust |
110 | | /// # type Signed = i32; |
111 | | /// # type Unsigned = u32; |
112 | | /// fn zigzag(v: Signed) -> Unsigned { |
113 | | /// match v { |
114 | | /// 0 => 0, |
115 | | /// // To avoid the edge case of Signed::min_value() |
116 | | /// // !n is equal to `-n - 1`, so this is: |
117 | | /// // !n * 2 + 1 = 2(-n - 1) + 1 = -2n - 2 + 1 = -2n - 1 |
118 | | /// v if v < 0 => !(v as Unsigned) * 2 - 1, |
119 | | /// v if v > 0 => (v as Unsigned) * 2, |
120 | | /// # _ => unreachable!() |
121 | | /// } |
122 | | /// } |
123 | | /// ``` |
124 | | /// |
125 | | /// And works such that: |
126 | | /// |
127 | | /// ```rust |
128 | | /// # let zigzag = |n: i64| -> u64 { |
129 | | /// # match n { |
130 | | /// # 0 => 0, |
131 | | /// # v if v < 0 => !(v as u64) * 2 + 1, |
132 | | /// # v if v > 0 => (v as u64) * 2, |
133 | | /// # _ => unreachable!(), |
134 | | /// # } |
135 | | /// # }; |
136 | | /// assert_eq!(zigzag(0), 0); |
137 | | /// assert_eq!(zigzag(-1), 1); |
138 | | /// assert_eq!(zigzag(1), 2); |
139 | | /// assert_eq!(zigzag(-2), 3); |
140 | | /// assert_eq!(zigzag(2), 4); |
141 | | /// // etc |
142 | | /// assert_eq!(zigzag(i64::min_value()), u64::max_value()); |
143 | | /// ``` |
144 | | /// |
145 | | /// Note that u256 and the like are unsupported by this format; if and when they are added to the |
146 | | /// language, they may be supported via the extension point given by the 255 byte. |
147 | 0 | pub const fn with_variable_int_encoding(self) -> Configuration<E, Varint, L> { |
148 | 0 | generate() |
149 | 0 | } |
150 | | |
151 | | /// Fixed-size integer encoding. |
152 | | /// |
153 | | /// * Fixed size integers are encoded directly |
154 | | /// * Enum discriminants are encoded as u32 |
155 | | /// * Lengths and usize are encoded as u64 |
156 | 0 | pub const fn with_fixed_int_encoding(self) -> Configuration<E, Fixint, L> { |
157 | 0 | generate() |
158 | 0 | } |
159 | | |
160 | | /// Sets the byte limit to `limit`. |
161 | 0 | pub const fn with_limit<const N: usize>(self) -> Configuration<E, I, Limit<N>> { |
162 | 0 | generate() |
163 | 0 | } |
164 | | |
165 | | /// Clear the byte limit. |
166 | 0 | pub const fn with_no_limit(self) -> Configuration<E, I, NoLimit> { |
167 | 0 | generate() |
168 | 0 | } |
169 | | } |
170 | | |
171 | | /// Indicates a type is valid for controlling the bincode configuration |
172 | | pub trait Config: |
173 | | InternalEndianConfig + InternalIntEncodingConfig + InternalLimitConfig + Copy + Clone |
174 | | { |
175 | | /// This configuration's Endianness |
176 | | fn endianness(&self) -> Endianness; |
177 | | |
178 | | /// This configuration's Integer Encoding |
179 | | fn int_encoding(&self) -> IntEncoding; |
180 | | |
181 | | /// This configuration's byte limit, or `None` if no limit is configured |
182 | | fn limit(&self) -> Option<usize>; |
183 | | } |
184 | | |
185 | | impl<T> Config for T |
186 | | where |
187 | | T: InternalEndianConfig + InternalIntEncodingConfig + InternalLimitConfig + Copy + Clone, |
188 | | { |
189 | 0 | fn endianness(&self) -> Endianness { |
190 | 0 | <T as InternalEndianConfig>::ENDIAN |
191 | 0 | } |
192 | | |
193 | 0 | fn int_encoding(&self) -> IntEncoding { |
194 | 0 | <T as InternalIntEncodingConfig>::INT_ENCODING |
195 | 0 | } |
196 | | |
197 | 0 | fn limit(&self) -> Option<usize> { |
198 | 0 | <T as InternalLimitConfig>::LIMIT |
199 | 0 | } |
200 | | } |
201 | | |
202 | | /// Encodes all integer types in big endian. |
203 | | #[derive(Copy, Clone)] |
204 | | pub struct BigEndian {} |
205 | | |
206 | | impl InternalEndianConfig for BigEndian { |
207 | | const ENDIAN: Endianness = Endianness::Big; |
208 | | } |
209 | | |
210 | | /// Encodes all integer types in little endian. |
211 | | #[derive(Copy, Clone)] |
212 | | pub struct LittleEndian {} |
213 | | |
214 | | impl InternalEndianConfig for LittleEndian { |
215 | | const ENDIAN: Endianness = Endianness::Little; |
216 | | } |
217 | | |
218 | | /// Use fixed-size integer encoding. |
219 | | #[derive(Copy, Clone)] |
220 | | pub struct Fixint {} |
221 | | |
222 | | impl InternalIntEncodingConfig for Fixint { |
223 | | const INT_ENCODING: IntEncoding = IntEncoding::Fixed; |
224 | | } |
225 | | |
226 | | /// Use variable integer encoding. |
227 | | #[derive(Copy, Clone)] |
228 | | pub struct Varint {} |
229 | | |
230 | | impl InternalIntEncodingConfig for Varint { |
231 | | const INT_ENCODING: IntEncoding = IntEncoding::Variable; |
232 | | } |
233 | | |
234 | | /// Sets an unlimited byte limit. |
235 | | #[derive(Copy, Clone)] |
236 | | pub struct NoLimit {} |
237 | | impl InternalLimitConfig for NoLimit { |
238 | | const LIMIT: Option<usize> = None; |
239 | | } |
240 | | |
241 | | /// Sets the byte limit to N. |
242 | | #[derive(Copy, Clone)] |
243 | | pub struct Limit<const N: usize> {} |
244 | | impl<const N: usize> InternalLimitConfig for Limit<N> { |
245 | | const LIMIT: Option<usize> = Some(N); |
246 | | } |
247 | | |
248 | | /// Endianness of a `Configuration`. |
249 | | #[derive(PartialEq, Eq)] |
250 | | #[non_exhaustive] |
251 | | pub enum Endianness { |
252 | | /// Little Endian encoding, see `LittleEndian`. |
253 | | Little, |
254 | | /// Big Endian encoding, see `BigEndian`. |
255 | | Big, |
256 | | } |
257 | | |
258 | | /// Integer Encoding of a `Configuration`. |
259 | | #[derive(PartialEq, Eq)] |
260 | | #[non_exhaustive] |
261 | | pub enum IntEncoding { |
262 | | /// Fixed Integer Encoding, see `Fixint`. |
263 | | Fixed, |
264 | | /// Variable Integer Encoding, see `Varint`. |
265 | | Variable, |
266 | | } |
267 | | |
268 | | mod internal { |
269 | | use super::{Configuration, Endianness, IntEncoding}; |
270 | | |
271 | | pub trait InternalEndianConfig { |
272 | | const ENDIAN: Endianness; |
273 | | } |
274 | | |
275 | | impl<E: InternalEndianConfig, I, L> InternalEndianConfig for Configuration<E, I, L> { |
276 | | const ENDIAN: Endianness = E::ENDIAN; |
277 | | } |
278 | | |
279 | | pub trait InternalIntEncodingConfig { |
280 | | const INT_ENCODING: IntEncoding; |
281 | | } |
282 | | |
283 | | impl<E, I: InternalIntEncodingConfig, L> InternalIntEncodingConfig for Configuration<E, I, L> { |
284 | | const INT_ENCODING: IntEncoding = I::INT_ENCODING; |
285 | | } |
286 | | |
287 | | pub trait InternalLimitConfig { |
288 | | const LIMIT: Option<usize>; |
289 | | } |
290 | | |
291 | | impl<E, I, L: InternalLimitConfig> InternalLimitConfig for Configuration<E, I, L> { |
292 | | const LIMIT: Option<usize> = L::LIMIT; |
293 | | } |
294 | | } |