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