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() {}