Skip to main content

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/// Falls back to the type specified in `#[repr(...)]` if not present.
190///
191/// ```
192/// # use bin_proto::{BitDecode, BitEncode};
193/// #[derive(BitDecode, BitEncode)]
194/// #[bin_proto(discriminant_type = u8)]
195/// enum Example {
196///     Variant1 = 1,
197///     Variant5 = 5,
198/// }
199/// ```
200///
201/// ```
202/// # use bin_proto::{BitDecode, BitEncode};
203/// #[derive(BitDecode, BitEncode)]
204/// #[repr(u8)]
205/// enum Example {
206///     Variant1 = 1,
207///     Variant5 = 5,
208/// }
209/// ```
210///
211/// ## `discriminant`
212/// `#[bin_proto(discriminant = <value>)]`
213/// - `<value>`: unique value of the discriminant's type
214///
215/// Specify the discriminant for a variant.
216///
217/// ```
218/// # use bin_proto::{BitDecode, BitEncode};
219/// #[derive(BitDecode, BitEncode)]
220/// #[bin_proto(discriminant_type = u8)]
221/// enum Example {
222///     #[bin_proto(discriminant = 1)]
223///     Variant1,
224///     Variant5 = 5,
225/// }
226/// ```
227///
228/// ## `other`
229/// `#[bin_proto(other)]`
230///
231/// Decode the specified variant if the discriminant doesn't match any other variants. A
232/// discriminant value can still be provided for the variant, and will be used when encoding.
233///
234/// ```
235/// # use bin_proto::{BitDecode, BitEncode};
236/// #[derive(BitDecode, BitEncode)]
237/// #[bin_proto(discriminant_type = u8)]
238/// enum Example {
239///     #[bin_proto(discriminant = 1)]
240///     Variant1,
241///     #[bin_proto(discriminant = 2, other)]
242///     CatchAll,
243/// }
244/// ```
245///
246/// ## `bits`
247/// `#[bin_proto(bits = <width>)]`
248///
249/// Determine width of field in bits.
250///
251/// **WARNING**: Bitfields disregard endianness and instead have the same endianness as the
252/// underlying [`BitRead`] / [`BitWrite`] instance. If you're using bitfields, you almost always
253/// want a big endian stream.
254///
255/// ```
256/// # use bin_proto::{BitDecode, BitEncode};
257/// #[derive(BitDecode, BitEncode)]
258/// struct Nibble(#[bin_proto(bits = 4)] u8);
259/// ```
260///
261/// ## `untagged`
262/// `#[bin_proto(untagged)]`
263///
264/// Variable-length field is final field in container, hence lacks a length prefix and should be
265/// read until eof.
266///
267/// ```
268/// # #[cfg(feature = "alloc")]
269/// # {
270/// # use bin_proto::{BitDecode, BitEncode};
271/// #[derive(BitDecode, BitEncode)]
272/// struct ReadToEnd(#[bin_proto(untagged)] Vec<u8>);
273/// # }
274/// ```
275///
276/// ## `tag`
277/// `#[bin_proto(tag = <expr>)]`
278/// - `<expr>`: arbitrary expression. Fields in parent container can be used without prefixing them
279///   with `self`.
280///
281/// Specify tag of field. The tag represents a length prefix for variable-length fields, and a
282/// boolean for [`Option`].
283///
284/// ```
285/// # #[cfg(feature = "alloc")]
286/// # {
287/// # use bin_proto::{BitDecode, BitEncode};
288/// #[derive(BitDecode, BitEncode)]
289/// struct WithElementsLength {
290///     count: u32,
291///     foo: bool,
292///     #[bin_proto(tag = count as usize)]
293///     data: Vec<u32>,
294/// }
295/// # }
296/// ```
297///
298/// ## `tag_type`
299/// `#[bin_proto(tag_type = <type>[, tag_value = <expr>]?[, tag_bits = <expr>]?)]`
300/// - `<type>`: tag's type
301/// - `<expr>`: arbitrary expression. Fields in parent container should be prefixed with `self`.
302///
303/// Specify tag of field. The tag represents a length prefix for variable-length fields, and a
304/// boolean for [`Option`]. The tag is placed directly before the field. The `tag_value` only has
305/// to be specified when deriving [`BitEncode`].
306///
307/// ```
308/// # #[cfg(feature = "alloc")]
309/// # {
310/// # use bin_proto::{BitDecode, BitEncode};
311/// #[derive(BitDecode, BitEncode)]
312/// struct WithElementsLength {
313///     #[bin_proto(tag_type = u16, tag_value = self.data.len() as u16, tag_bits = 13)]
314///     data: Vec<u32>,
315/// }
316/// # }
317/// ```
318///
319/// ## `write_value`
320/// `#[bin_proto(write_value = <expr>)]`
321/// - `<expr>`: An expression that can be coerced to the field type. Fields in parent container
322///   should be prefixed with `self`.
323///
324/// Specify an expression that should be used as the field's value for writing.
325///
326/// ```
327/// # #[cfg(feature = "alloc")]
328/// # {
329/// # use bin_proto::{BitDecode, BitEncode};
330/// #[derive(BitDecode, BitEncode)]
331/// struct WithElementsLengthAuto {
332///     #[bin_proto(write_value = self.data.len() as u32)]
333///     count: u32,
334///     foo: bool,
335///     #[bin_proto(tag = count as usize)]
336///     data: Vec<u32>,
337/// }
338/// # }
339/// ```
340///
341/// ## `ctx`
342/// `#[bin_proto(ctx = <type>)[, ctx_generics(<generic>[, <generic>]*)]?]`
343/// - `<type>`: The type of the context. Either a concrete type, or one of the container's generics
344/// - `<generic>`: Any generics used by the context type, with optional bounds. E.g.
345///   `T: Copy` for a [`Vec<T>`](alloc::vec::Vec) context.
346///
347/// Specify the type of context that will be passed to codec functions.
348///
349/// ```
350/// # #[cfg(feature = "alloc")]
351/// # {
352/// # use bin_proto::{BitDecode, BitEncode, BitEncodeExt};
353/// struct Ctx;
354///
355/// struct NeedsCtx;
356///
357/// impl BitDecode<Ctx> for NeedsCtx {
358///     fn decode<R, E>(
359///         _read: &mut R,
360///         _ctx: &mut Ctx,
361///         _tag: (),
362///     ) -> bin_proto::Result<Self>
363///     where
364///         R: bin_proto::BitRead,
365///         E: bin_proto::Endianness,
366///     {
367///         // Use ctx here
368///         Ok(Self)
369///     }
370/// }
371///
372/// impl BitEncode<Ctx> for NeedsCtx {
373///     fn encode<W, E>(
374///         &self,
375///         _write: &mut W,
376///         _ctx: &mut Ctx,
377///         _tag: (),
378///     ) -> bin_proto::Result<()>
379///     where
380///         W: bin_proto::BitWrite,
381///         E: bin_proto::Endianness,
382///     {
383///         // Use ctx here
384///         Ok(())
385///     }
386/// }
387///
388/// #[derive(BitDecode, BitEncode)]
389/// #[bin_proto(ctx = Ctx)]
390/// struct WithCtx(NeedsCtx);
391///
392/// WithCtx(NeedsCtx)
393///     .encode_bytes_ctx(bin_proto::BigEndian, &mut Ctx, ())
394///     .unwrap();
395/// # }
396/// ```
397///
398/// ```
399/// # use bin_proto::{BitDecode, BitEncode};
400/// # use std::marker::PhantomData;
401/// #[derive(BitDecode, BitEncode)]
402/// #[bin_proto(ctx = Ctx)]
403/// struct NestedCodec<Ctx, A: BitDecode<Ctx> + BitEncode<Ctx>>(A, PhantomData<Ctx>);
404/// ```
405///
406/// ```
407/// # use bin_proto::{BitDecode, BitEncode};
408/// struct Ctx<'a, T: Copy>(&'a T);
409///
410/// #[derive(BitDecode, BitEncode)]
411/// #[bin_proto(ctx = Ctx<'a, T>, ctx_generics('a, T: Copy))]
412/// struct WithCtx;
413/// ```
414///
415/// ## `ctx_bounds`
416/// `#[bin_proto(ctx_bounds(<bound>[, <bound>]*)[, ctx_generics(<generic>[, <generic>]*)]?)]`
417/// - `<bounds>`: Trait bounds that must be satisfied by the context
418/// - `<generic>`: Any generics used by the context type. E.g. `'a` for a context with a
419///   [`From<&'a i32>`](From) bound.
420///
421/// Specify the trait bounds of context that will be passed to codec functions.
422///
423/// ```
424/// # use bin_proto::{BitDecode, BitEncode};
425/// trait CtxTrait {};
426///
427/// struct NeedsCtx;
428///
429/// impl<Ctx: CtxTrait> BitDecode<Ctx> for NeedsCtx {
430///     fn decode<R, E>(
431///         _read: &mut R,
432///         _ctx: &mut Ctx,
433///         _tag: (),
434///     ) -> bin_proto::Result<Self>
435///     where
436///         R: bin_proto::BitRead,
437///         E: bin_proto::Endianness,
438///     {
439///         // Use ctx here
440///         Ok(Self)
441///     }
442///}
443///
444/// impl<Ctx: CtxTrait> BitEncode<Ctx> for NeedsCtx {
445///     fn encode<W, E>(
446///         &self,
447///         _write: &mut W,
448///         _ctx: &mut Ctx,
449///         _tag: (),
450///     ) -> bin_proto::Result<()>
451///     where
452///         W: bin_proto::BitWrite,
453///         E: bin_proto::Endianness,
454///     {
455///         // Use ctx here
456///         Ok(())
457///     }
458/// }
459///
460/// #[derive(BitDecode, BitEncode)]
461/// #[bin_proto(ctx_bounds(CtxTrait))]
462/// struct WithCtx(NeedsCtx);
463/// ```
464///
465/// ```
466/// # use bin_proto::{BitDecode, BitEncode};
467/// #[derive(BitDecode, BitEncode)]
468/// #[bin_proto(ctx_bounds(From<&'a i32>), ctx_generics('a))]
469/// struct WithCtx;
470/// ```
471///
472/// ## `skip_encode`
473/// `#[bin_proto(skip_encode)]`
474///
475/// If applied to a field, skip the field when encoding. If applied to an enum variant, return an
476/// Error if the variant is attempted to be encoded.
477///
478/// ```
479/// # use bin_proto::{BitDecode, BitEncode};
480/// #[derive(BitDecode, BitEncode)]
481/// struct Struct(#[bin_proto(skip_encode)] u8);
482/// ```
483///
484/// ```
485/// # use bin_proto::BitEncode;
486/// #[derive(BitEncode)]
487/// #[bin_proto(discriminant_type = u8)]
488/// enum Enum {
489///     #[bin_proto(skip_encode)]
490///     Skip
491/// }
492/// ```
493///
494/// ## `skip_decode`
495/// `#[bin_proto(skip_decode)]`
496///
497/// If applied to a field, use [`Default::default`] instead of attempting to read field. If applied
498/// to an enum variant, don't generate code for decoding.
499///
500/// ```
501/// # use bin_proto::{BitDecode, BitEncode};
502/// #[derive(BitDecode, BitEncode)]
503/// struct Struct(#[bin_proto(skip_decode)] u8);
504/// ```
505///
506/// ```
507/// # use bin_proto::BitDecode;
508/// #[derive(BitDecode)]
509/// #[bin_proto(discriminant_type = u8)]
510/// enum Enum {
511///     #[bin_proto(skip_decode)]
512///     Skip
513/// }
514/// ```
515///
516/// ## `skip`
517/// `#[bin_proto(skip)]`
518///
519/// Equivalent to combining [`skip_encode`](#skip_encode) and [`skip_decode`](#skip_decode).
520///
521/// ```
522/// # use bin_proto::{BitDecode, BitEncode};
523/// #[derive(BitDecode, BitEncode)]
524/// struct Struct(#[bin_proto(skip)] u8);
525/// ```
526///
527/// ```
528/// # use bin_proto::{BitDecode, BitEncode};
529/// #[derive(BitDecode, BitEncode)]
530/// #[bin_proto(discriminant_type = u8)]
531/// enum Enum {
532///     #[bin_proto(skip)]
533///     Skip
534/// }
535/// ```
536///
537/// ## `pad_before`
538/// `#[bin_proto(pad_before = <expr>)]`
539///
540/// Insert 0 bits when writing and skip bits when reading, prior to processing the field.
541///
542/// ```
543/// # use bin_proto::{BitDecode, BitEncode};
544/// #[derive(BitDecode, BitEncode)]
545/// struct Struct(#[bin_proto(pad_before = 3)] u8);
546/// ```
547///
548/// ## `pad_after`
549/// `#[bin_proto(pad_after = <expr>)]`
550///
551/// Insert 0 bits when writing and skip bits when reading, after processing the field.
552///
553/// ```
554/// # use bin_proto::{BitDecode, BitEncode};
555/// #[derive(BitDecode, BitEncode)]
556/// struct Struct(#[bin_proto(pad_after = 3)] u8);
557/// ```
558///
559/// ## `magic`
560/// `#[bin_proto(magic = <expr>)]`
561/// - `<expr>`: Must evaluate to `&[u8; _]`
562///
563/// Indicates that the value must be present immediately preceding the field or struct.
564///
565/// ```
566/// # use bin_proto::{BitDecode, BitEncode};
567/// #[derive(BitDecode, BitEncode)]
568/// #[bin_proto(magic = &[0x01, 0x02, 0x03])]
569/// struct Magic(#[bin_proto(magic = b"123")] u8);
570/// ```
571#[cfg(feature = "derive")]
572pub use bin_proto_derive::{BitDecode, BitEncode};
573
574#[macro_use]
575mod codec;
576
577mod discriminable;
578mod error;
579mod impls;
580pub mod util;
581
582pub extern crate bitstream_io;
583
584/// A marker for [`BitEncode`] implementors that don't prepend their tag, and [`BitDecode`]
585/// implementors that usually have a tag, but can be read to EOF
586pub struct Untagged;
587
588/// A marker for [`BitDecode`] implementors that require a tag.
589pub struct Tag<T>(pub T);
590
591/// A marker for [`BitDecode`] and [`BitEncode`] implementors that support bitfield operations.
592pub struct Bits<const C: u32>;
593
594/// ```compile_fail
595/// # use bin_proto::{BitDecode, BitEncode};
596/// #[derive(BitDecode, BitEncode)]
597/// struct MutuallyExclusiveAttrs {
598///     pub length: u8,
599///     #[bin_proto(untagged)]
600///     #[bin_proto(tag = length as usize)]
601///     pub reason: alloc::string::String,
602/// }
603/// ```
604#[cfg(all(feature = "derive", feature = "alloc", doctest))]
605#[allow(unused)]
606fn compile_fail_if_multiple_exclusive_attrs() {}