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