bin_proto/
lib.rs

1//! Conversion to/from binary for arbitrary types. With `no_std` and `no_alloc` support.
2//!
3//! For more information about `#[derive(BitDecode, BitEncode)]` and its attributes, see
4//! [`macro@BitDecode`] or [`macro@BitEncode`].
5//!
6//! # Example
7//!
8//! ```
9//! # #[cfg(all(feature = "derive", feature = "alloc"))]
10//! # {
11//! # use bin_proto::{BitDecode, BitEncode, BitCodec};
12//! #[derive(Debug, BitDecode, BitEncode, PartialEq)]
13//! #[bin_proto(discriminant_type = u8)]
14//! #[bin_proto(bits = 4)]
15//! enum E {
16//!     V1 = 1,
17//!     #[bin_proto(discriminant = 4)]
18//!     V4,
19//! }
20//!
21//! #[derive(Debug, BitDecode, BitEncode, PartialEq)]
22//! struct S {
23//!     #[bin_proto(bits = 1)]
24//!     bitflag: bool,
25//!     #[bin_proto(bits = 3)]
26//!     bitfield: u8,
27//!     enum_: E,
28//!     #[bin_proto(write_value = self.arr.len() as u8)]
29//!     arr_len: u8,
30//!     #[bin_proto(tag = arr_len as usize)]
31//!     arr: Vec<u8>,
32//!     #[bin_proto(tag_type = u16, tag_value = self.prefixed_arr.len() as u16)]
33//!     prefixed_arr: Vec<u8>,
34//!     #[bin_proto(untagged)]
35//!     read_to_end: Vec<u8>,
36//! }
37//!
38//! assert_eq!(
39//!     S::decode_bytes(&[
40//!         0b1000_0000 // bitflag: true (1)
41//!        | 0b101_0000 // bitfield: 5 (101)
42//!            | 0b0001, // enum_: V1 (0001)
43//!         0x02, // arr_len: 2
44//!         0x21, 0x37, // arr: [0x21, 0x37]
45//!         0x00, 0x01, 0x33, // prefixed_arr: [0x33]
46//!         0x01, 0x02, 0x03, // read_to_end: [0x01, 0x02, 0x03]
47//!     ], bin_proto::BigEndian).unwrap().0,
48//!     S {
49//!         bitflag: true,
50//!         bitfield: 5,
51//!         enum_: E::V1,
52//!         arr_len: 2,
53//!         arr: vec![0x21, 0x37],
54//!         prefixed_arr: vec![0x33],
55//!         read_to_end: vec![0x01, 0x02, 0x03],
56//!     }
57//! );
58//! # }
59//! ```
60//!
61//! # Manual Implementations
62//!
63//! The [`macro@BitDecode`] and [`macro@BitEncode`] derive macros support the most common use-cases,
64//! but it may sometimes be necessary to manually implement [`BitEncode`] or [`BitDecode`]. Both
65//! traits have two generic parameters:
66//! - `Ctx`: A mutable variable passed recursively down the codec chain
67//! - `Tag`: A tag for specifying additional behavior
68//!
69//! `Tag` can have any type. The following are used throughout `bin-proto` and ensure
70//! interoperability:
71//! - [`Tag`]: Specifies that an additional tag is required during decoding, such as a length prefix
72//!   for a [`Vec`](::alloc::vec::Vec), or a discriminant of an `enum`
73//! - [`Untagged`]: Specifies that the type has a tag used during decoding, but this tag is not
74//!   written during encoding
75//! - [`Bits`]: Specified that the type is a bitfield, and can have a variable number of bits
76//!
77//! # Features
78//!
79//! - `std` — Enables support for types in the standard library.
80//! - `alloc` — Enables support for types in the `alloc` crate.
81//! - `derive` — Provides procedural macros for deriving traits [`BitEncode`] and [`BitDecode`].
82//! - `prepend-tags` — Enables tag prepending for common types ([`Option`], [`str`], etc.), removing
83//!   the need for explicit tag specification for encoding/decoding. **WARNING**: length tags are
84//!   encoded as `usize`, meaning they may vary if targets have different pointer widths.
85
86#![cfg_attr(docsrs, feature(doc_cfg))]
87#![cfg_attr(docsrs, feature(rustdoc_internals))]
88#![cfg_attr(docsrs, allow(internal_features))]
89#![no_std]
90#![deny(
91    missing_docs,
92    clippy::pedantic,
93    clippy::nursery,
94    clippy::cargo,
95    clippy::unwrap_used,
96    clippy::expect_used,
97    clippy::suspicious,
98    clippy::complexity,
99    clippy::perf,
100    clippy::style
101)]
102#![allow(clippy::module_name_repetitions, clippy::missing_errors_doc)]
103
104#[cfg(feature = "alloc")]
105extern crate alloc;
106#[cfg(feature = "std")]
107extern crate std;
108
109pub use self::codec::BitCodec;
110pub use self::codec::{BitDecode, BitDecodeExt, BitEncode, BitEncodeExt};
111pub use self::discriminable::Discriminable;
112pub use self::error::{Error, Result};
113pub use bitstream_io::{BigEndian, BitRead, BitWrite, Endianness, LittleEndian};
114
115/// Derive the [`BitDecode`] and [`BitEncode`] traits.
116///
117/// # Scopes
118///
119/// ```ignore
120/// #[container, enum]
121/// enum Enum {
122///     #[variant]
123///     Variant {
124///         #[field]
125///         field: Type,
126///     }
127/// }
128///
129/// #[container, struct]
130/// struct Struct {
131///     #[field]
132///     field: Type,
133/// }
134/// ```
135///
136/// # Using Context
137///
138/// The [`ctx`](#ctx) and [`ctx_bounds`](#ctx_bounds) attributes can be used to specify additional
139/// context used during codec. The context can be accessed as `__ctx`, and the tag as `__tag` in any
140/// attribute macro's `<expr>`, for example in [`tag`](#tag).
141///
142/// ```
143/// # #[cfg(feature = "alloc")]
144/// # {
145/// # use bin_proto::{BitDecode, BitEncode};
146/// struct Ctx {
147///     n: u32,
148/// }
149///
150/// #[derive(BitDecode, BitEncode)]
151/// #[bin_proto(ctx = Ctx)]
152/// struct WithElementsLength {
153///     count: u32,
154///     foo: bool,
155///     #[bin_proto(tag = count * __ctx.n)]
156///     data: Vec<u32>,
157/// }
158/// # }
159/// ```
160///
161/// # Attributes
162///
163/// | Attribute | Scope | Applicability |
164/// |-|-|-|
165/// | [`discriminant_type`](#discriminant_type) | enum | rw |
166/// | [`discriminant`](#discriminant) | variant | rw |
167/// | [`other`](#other) | variant | r |
168/// | [`bits`](#bits) | field, enum | rw |
169/// | [`untagged`](#untagged) | field | rw |
170/// | [`tag`](#tag) | field | rw |
171/// | [`tag_type`](#tag_type) | field | rw |
172/// | [`write_value`](#write_value) | field | w |
173/// | [`ctx`](#ctx) | container | rw |
174/// | [`ctx_bounds`](#ctx_bounds) | container | rw |
175/// | [`skip_encode`](#skip_encode) | field, variant | w |
176/// | [`skip_decode`](#skip_decode) | field, variant | r |
177/// | [`skip`](#skip) | field, variant | rw |
178/// | [`pad_before`](#pad_before) | field, struct | rw |
179/// | [`pad_after`](#pad_after) | field, struct | rw |
180/// | [`magic`](#magic) | field, struct | rw |
181///
182/// ## `discriminant_type`
183/// `#[bin_proto(discriminant_type = <type>)]`
184/// - `<type>`: an arbitrary type that implements [`BitDecode`] or [`BitEncode`]
185///
186/// Specify if enum variant should be determined by a string or interger representation of its
187/// discriminant.
188///
189/// ```
190/// # use bin_proto::{BitDecode, BitEncode};
191/// #[derive(BitDecode, BitEncode)]
192/// #[bin_proto(discriminant_type = u8)]
193/// enum Example {
194///     Variant1 = 1,
195///     Variant5 = 5,
196/// }
197/// ```
198///
199/// ## `discriminant`
200/// `#[bin_proto(discriminant = <value>)]`
201/// - `<value>`: unique value of the discriminant's type
202///
203/// Specify the discriminant for a variant.
204///
205/// ```
206/// # use bin_proto::{BitDecode, BitEncode};
207/// #[derive(BitDecode, BitEncode)]
208/// #[bin_proto(discriminant_type = u8)]
209/// enum Example {
210///     #[bin_proto(discriminant = 1)]
211///     Variant1,
212///     Variant5 = 5,
213/// }
214/// ```
215///
216/// ## `other`
217/// `#[bin_proto(other)]`
218///
219/// Decode the specified variant if the discriminant doesn't match any other variants. A
220/// discriminant value can still be provided for the variant, and will be used when encoding.
221///
222/// ```
223/// # use bin_proto::{BitDecode, BitEncode};
224/// #[derive(BitDecode, BitEncode)]
225/// #[bin_proto(discriminant_type = u8)]
226/// enum Example {
227///     #[bin_proto(discriminant = 1)]
228///     Variant1,
229///     #[bin_proto(discriminant = 2, other)]
230///     CatchAll,
231/// }
232/// ```
233///
234/// ## `bits`
235/// `#[bin_proto(bits = <width>)]`
236///
237/// Determine width of field in bits.
238///
239/// **WARNING**: Bitfields disregard endianness and instead have the same endianness as the
240/// underlying [`BitRead`] / [`BitWrite`] instance. If you're using bitfields, you almost always
241/// want a big endian stream.
242///
243/// ```
244/// # use bin_proto::{BitDecode, BitEncode};
245/// #[derive(BitDecode, BitEncode)]
246/// struct Nibble(#[bin_proto(bits = 4)] u8);
247/// ```
248///
249/// ## `untagged`
250/// `#[bin_proto(untagged)]`
251///
252/// Variable-length field is final field in container, hence lacks a length prefix and should be
253/// read until eof.
254///
255/// ```
256/// # #[cfg(feature = "alloc")]
257/// # {
258/// # use bin_proto::{BitDecode, BitEncode};
259/// #[derive(BitDecode, BitEncode)]
260/// struct ReadToEnd(#[bin_proto(untagged)] Vec<u8>);
261/// # }
262/// ```
263///
264/// ## `tag`
265/// `#[bin_proto(tag = <expr>)]`
266/// - `<expr>`: arbitrary expression. Fields in parent container can be used without prefixing them
267///   with `self`.
268///
269/// Specify tag of field. The tag represents a length prefix for variable-length fields, and a
270/// boolean for [`Option`].
271///
272/// ```
273/// # #[cfg(feature = "alloc")]
274/// # {
275/// # use bin_proto::{BitDecode, BitEncode};
276/// #[derive(BitDecode, BitEncode)]
277/// struct WithElementsLength {
278///     count: u32,
279///     foo: bool,
280///     #[bin_proto(tag = count as usize)]
281///     data: Vec<u32>,
282/// }
283/// # }
284/// ```
285///
286/// ## `tag_type`
287/// `#[bin_proto(tag_type = <type>[, tag_value = <expr>]?[, tag_bits = <expr>]?)]`
288/// - `<type>`: tag's type
289/// - `<expr>`: arbitrary expression. Fields in parent container should be prefixed with `self`.
290///
291/// Specify tag of field. The tag represents a length prefix for variable-length fields, and a
292/// boolean for [`Option`]. The tag is placed directly before the field. The `tag_value` only has
293/// to be specified when deriving [`BitEncode`].
294///
295/// ```
296/// # #[cfg(feature = "alloc")]
297/// # {
298/// # use bin_proto::{BitDecode, BitEncode};
299/// #[derive(BitDecode, BitEncode)]
300/// struct WithElementsLength {
301///     #[bin_proto(tag_type = u16, tag_value = self.data.len() as u16, tag_bits = 13)]
302///     data: Vec<u32>,
303/// }
304/// # }
305/// ```
306///
307/// ## `write_value`
308/// `#[bin_proto(write_value = <expr>)]`
309/// - `<expr>`: An expression that can be coerced to the field type. Fields in parent container
310///   should be prefixed with `self`.
311///
312/// Specify an expression that should be used as the field's value for writing.
313///
314/// ```
315/// # #[cfg(feature = "alloc")]
316/// # {
317/// # use bin_proto::{BitDecode, BitEncode};
318/// #[derive(BitDecode, BitEncode)]
319/// struct WithElementsLengthAuto {
320///     #[bin_proto(write_value = self.data.len() as u32)]
321///     count: u32,
322///     foo: bool,
323///     #[bin_proto(tag = count as usize)]
324///     data: Vec<u32>,
325/// }
326/// # }
327/// ```
328///
329/// ## `ctx`
330/// `#[bin_proto(ctx = <type>)[, ctx_generics(<generic>[, <generic>]*)]?]`
331/// - `<type>`: The type of the context. Either a concrete type, or one of the container's generics
332/// - `<generic>`: Any generics used by the context type, with optional bounds. E.g.
333///   `T: Copy` for a [`Vec<T>`](alloc::vec::Vec) context.
334///
335/// Specify the type of context that will be passed to codec functions.
336///
337/// ```
338/// # #[cfg(feature = "alloc")]
339/// # {
340/// # use bin_proto::{BitDecode, BitEncode, BitEncodeExt};
341/// struct Ctx;
342///
343/// struct NeedsCtx;
344///
345/// impl BitDecode<Ctx> for NeedsCtx {
346///     fn decode<R, E>(
347///         _read: &mut R,
348///         _ctx: &mut Ctx,
349///         _tag: (),
350///     ) -> bin_proto::Result<Self>
351///     where
352///         R: bin_proto::BitRead,
353///         E: bin_proto::Endianness,
354///     {
355///         // Use ctx here
356///         Ok(Self)
357///     }
358/// }
359///
360/// impl BitEncode<Ctx> for NeedsCtx {
361///     fn encode<W, E>(
362///         &self,
363///         _write: &mut W,
364///         _ctx: &mut Ctx,
365///         _tag: (),
366///     ) -> bin_proto::Result<()>
367///     where
368///         W: bin_proto::BitWrite,
369///         E: bin_proto::Endianness,
370///     {
371///         // Use ctx here
372///         Ok(())
373///     }
374/// }
375///
376/// #[derive(BitDecode, BitEncode)]
377/// #[bin_proto(ctx = Ctx)]
378/// struct WithCtx(NeedsCtx);
379///
380/// WithCtx(NeedsCtx)
381///     .encode_bytes_ctx(bin_proto::BigEndian, &mut Ctx, ())
382///     .unwrap();
383/// # }
384/// ```
385///
386/// ```
387/// # use bin_proto::{BitDecode, BitEncode};
388/// # use std::marker::PhantomData;
389/// #[derive(BitDecode, BitEncode)]
390/// #[bin_proto(ctx = Ctx)]
391/// struct NestedCodec<Ctx, A: BitDecode<Ctx> + BitEncode<Ctx>>(A, PhantomData<Ctx>);
392/// ```
393///
394/// ```
395/// # use bin_proto::{BitDecode, BitEncode};
396/// struct Ctx<'a, T: Copy>(&'a T);
397///
398/// #[derive(BitDecode, BitEncode)]
399/// #[bin_proto(ctx = Ctx<'a, T>, ctx_generics('a, T: Copy))]
400/// struct WithCtx;
401/// ```
402///
403/// ## `ctx_bounds`
404/// `#[bin_proto(ctx_bounds(<bound>[, <bound>]*)[, ctx_generics(<generic>[, <generic>]*)]?)]`
405/// - `<bounds>`: Trait bounds that must be satisfied by the context
406/// - `<generic>`: Any generics used by the context type. E.g. `'a` for a context with a
407///   [`From<&'a i32>`](From) bound.
408///
409/// Specify the trait bounds of context that will be passed to codec functions.
410///
411/// ```
412/// # use bin_proto::{BitDecode, BitEncode};
413/// trait CtxTrait {};
414///
415/// struct NeedsCtx;
416///
417/// impl<Ctx: CtxTrait> BitDecode<Ctx> for NeedsCtx {
418///     fn decode<R, E>(
419///         _read: &mut R,
420///         _ctx: &mut Ctx,
421///         _tag: (),
422///     ) -> bin_proto::Result<Self>
423///     where
424///         R: bin_proto::BitRead,
425///         E: bin_proto::Endianness,
426///     {
427///         // Use ctx here
428///         Ok(Self)
429///     }
430///}
431///
432/// impl<Ctx: CtxTrait> BitEncode<Ctx> for NeedsCtx {
433///     fn encode<W, E>(
434///         &self,
435///         _write: &mut W,
436///         _ctx: &mut Ctx,
437///         _tag: (),
438///     ) -> bin_proto::Result<()>
439///     where
440///         W: bin_proto::BitWrite,
441///         E: bin_proto::Endianness,
442///     {
443///         // Use ctx here
444///         Ok(())
445///     }
446/// }
447///
448/// #[derive(BitDecode, BitEncode)]
449/// #[bin_proto(ctx_bounds(CtxTrait))]
450/// struct WithCtx(NeedsCtx);
451/// ```
452///
453/// ```
454/// # use bin_proto::{BitDecode, BitEncode};
455/// #[derive(BitDecode, BitEncode)]
456/// #[bin_proto(ctx_bounds(From<&'a i32>), ctx_generics('a))]
457/// struct WithCtx;
458/// ```
459///
460/// ## `skip_encode`
461/// `#[bin_proto(skip_encode)]`
462///
463/// If applied to a field, skip the field when encoding. If applied to an enum variant, return an
464/// Error if the variant is attempted to be encoded.
465///
466/// ```
467/// # use bin_proto::{BitDecode, BitEncode};
468/// #[derive(BitDecode, BitEncode)]
469/// struct Struct(#[bin_proto(skip_encode)] u8);
470/// ```
471///
472/// ```
473/// # use bin_proto::BitEncode;
474/// #[derive(BitEncode)]
475/// #[bin_proto(discriminant_type = u8)]
476/// enum Enum {
477///     #[bin_proto(skip_encode)]
478///     Skip
479/// }
480/// ```
481///
482/// ## `skip_decode`
483/// `#[bin_proto(skip_decode)]`
484///
485/// If applied to a field, use [`Default::default`] instead of attempting to read field. If applied
486/// to an enum variant, don't generate code for decoding.
487///
488/// ```
489/// # use bin_proto::{BitDecode, BitEncode};
490/// #[derive(BitDecode, BitEncode)]
491/// struct Struct(#[bin_proto(skip_decode)] u8);
492/// ```
493///
494/// ```
495/// # use bin_proto::BitDecode;
496/// #[derive(BitDecode)]
497/// #[bin_proto(discriminant_type = u8)]
498/// enum Enum {
499///     #[bin_proto(skip_decode)]
500///     Skip
501/// }
502/// ```
503///
504/// ## `skip`
505/// `#[bin_proto(skip)]`
506///
507/// Equivalent to combining [`skip_encode`](#skip_encode) and [`skip_decode`](#skip_decode).
508///
509/// ```
510/// # use bin_proto::{BitDecode, BitEncode};
511/// #[derive(BitDecode, BitEncode)]
512/// struct Struct(#[bin_proto(skip)] u8);
513/// ```
514///
515/// ```
516/// # use bin_proto::{BitDecode, BitEncode};
517/// #[derive(BitDecode, BitEncode)]
518/// #[bin_proto(discriminant_type = u8)]
519/// enum Enum {
520///     #[bin_proto(skip)]
521///     Skip
522/// }
523/// ```
524///
525/// ## `pad_before`
526/// `#[bin_proto(pad_before = <expr>)]`
527///
528/// Insert 0 bits when writing and skip bits when reading, prior to processing the field.
529///
530/// ```
531/// # use bin_proto::{BitDecode, BitEncode};
532/// #[derive(BitDecode, BitEncode)]
533/// struct Struct(#[bin_proto(pad_before = 3)] u8);
534/// ```
535///
536/// ## `pad_after`
537/// `#[bin_proto(pad_after = <expr>)]`
538///
539/// Insert 0 bits when writing and skip bits when reading, after processing the field.
540///
541/// ```
542/// # use bin_proto::{BitDecode, BitEncode};
543/// #[derive(BitDecode, BitEncode)]
544/// struct Struct(#[bin_proto(pad_after = 3)] u8);
545/// ```
546///
547/// ## `magic`
548/// `#[bin_proto(magic = <expr>)]`
549/// - `<expr>`: Must evaluate to `&[u8; _]`
550///
551/// Indicates that the value must be present immediately preceding the field or struct.
552///
553/// ```
554/// # use bin_proto::{BitDecode, BitEncode};
555/// #[derive(BitDecode, BitEncode)]
556/// #[bin_proto(magic = &[0x01, 0x02, 0x03])]
557/// struct Magic(#[bin_proto(magic = b"123")] u8);
558/// ```
559#[cfg(feature = "derive")]
560pub use bin_proto_derive::{BitDecode, BitEncode};
561
562#[macro_use]
563mod codec;
564
565mod discriminable;
566mod error;
567mod impls;
568mod util;
569
570pub extern crate bitstream_io;
571
572/// A marker for [`BitEncode`] implementors that don't prepend their tag, and [`BitDecode`]
573/// implementors that usually have a tag, but can be read to EOF
574pub struct Untagged;
575
576/// A marker for [`BitDecode`] implementors that require a tag.
577pub struct Tag<T>(pub T);
578
579/// A marker for [`BitDecode`] and [`BitEncode`] implementors that support bitfield operations.
580pub struct Bits<const C: u32>;
581
582/// ```compile_fail
583/// # use bin_proto::{BitDecode, BitEncode};
584/// #[derive(BitDecode, BitEncode)]
585/// struct MutuallyExclusiveAttrs {
586///     pub length: u8,
587///     #[bin_proto(untagged)]
588///     #[bin_proto(tag = length as usize)]
589///     pub reason: alloc::string::String,
590/// }
591/// ```
592#[cfg(all(feature = "derive", feature = "alloc", doctest))]
593#[allow(unused)]
594fn compile_fail_if_multiple_exclusive_attrs() {}