1
0
Fork 0
mirror of https://codeberg.org/icewind/bitbuffer.git synced 2026-06-03 16:44:06 +02:00

doc updates

This commit is contained in:
Robin Appelman 2021-07-15 23:10:42 +02:00
commit 1ace766dda
3 changed files with 183 additions and 24 deletions

View file

@ -1,6 +1,6 @@
//! Tools for reading data types of arbitrary bit length and might not be byte-aligned in the source data
//! Tools for reading and writing data types of arbitrary bit length and might not be byte-aligned in the source data
//!
//! The main way of handling with the binary data is to first create a [`BitReadBuffer`]
//! The main way of reading the binary data is to first create a [`BitReadBuffer`]
//! ,wrap it into a [`BitReadStream`] and then read from the stream.
//!
//! Once you have a BitStream, there are 2 different approaches of reading data
@ -12,13 +12,22 @@
//!
//! The [`BitRead`] and [`BitReadSized`] traits can be used with `#[derive]` if all fields implement [`BitRead`] or [`BitReadSized`].
//!
//! For writing the data you wrap the output `Vec` into a [`BitWriteStream`] which can then be used in a manner similar to the [`BitReadStream`]
//!
//! - write primitives, Strings and byte arrays, using [`write_bool`], [`write_int`], [`write_float`], [`write_bytes`] and [`write_string`]
//! - write any type implementing the [`BitWrite`] or [`BitWriteSized`] traits using [`write`] and [`write_sized`]
//! - [`BitWrite`] is for types that can be written without requiring any size info (e.g. null-terminal strings, floats, whole integers, etc)
//! - [`BitWriteSized`] is for types that require external sizing information to be written (fixed length strings, arbitrary length integers
//!
//! Just like the read counterparts, [`BitWrite`] and [`BitWriteSized`] traits can be used with `#[derive]` if all fields implement [`BitWrite`] or [`BitWriteSized`].
//!
//! # Examples
//!
//! ```
//! # use bitbuffer::Result;
//! use bitbuffer::{BitReadBuffer, LittleEndian, BitReadStream, BitRead};
//! use bitbuffer::{BitReadBuffer, LittleEndian, BitReadStream, BitRead, BitWrite, BitWriteStream};
//!
//! #[derive(BitRead)]
//! #[derive(BitRead, BitWrite)]
//! struct ComplexType {
//! first: u8,
//! #[size = 15]
@ -35,22 +44,34 @@
//! let mut stream = BitReadStream::new(buffer);
//! let value: u8 = stream.read_int(7)?;
//! let complex: ComplexType = stream.read()?;
//!
//! let mut write_bytes = vec![];
//! let mut write_stream = BitWriteStream::new(&mut write_bytes, LittleEndian);
//! write_stream.write_int(12, 7)?;
//! write_stream.write(&ComplexType {
//! first: 55,
//! second: 12,
//! third: true
//! })?;
//! #
//! # Ok(())
//! # }
//! ```
//!
//! [`BitReadBuffer`]: struct.BitReadBuffer.html
//! [`BitReadStream`]: struct.BitReadStream.html
//! [`read_bool`]: struct.BitStream.html#method.read_bool
//! [`read_int`]: struct.BitStream.html#method.read_int
//! [`read_float`]: struct.BitStream.html#method.read_float
//! [`read_bytes`]: struct.BitStream.html#method.read_bytes
//! [`read_string`]: struct.BitStream.html#method.read_string
//! [`read`]: struct.BitStream.html#method.read
//! [`read_sized`]: struct.BitStream.html#method.read_sized
//! [`BitRead`]: trait.BitRead.html
//! [`BitReadSized`]: trait.BitReadSized.html
//! [`read_bool`]: BitReadStream::read_bool
//! [`read_int`]: BitReadStream::read_int
//! [`read_float`]: BitReadStream::read_float
//! [`read_bytes`]: BitReadStream::read_bytes
//! [`read_string`]: BitReadStream::read_string
//! [`read`]: BitReadStream::read
//! [`read_sized`]: BitReadStream::read_sized
//! [`write_bool`]: BitWriteStream::write_bool
//! [`write_int`]: BitWriteStream::write_int
//! [`write_float`]: BitWriteStream::write_float
//! [`write_bytes`]: BitWriteStream::write_bytes
//! [`write_string`]: BitWriteStream::write_string
//! [`write`]: BitWriteStream::write
//! [`write_sized`]: BitWriteStream::write_sized
#![warn(missing_docs)]
@ -75,7 +96,7 @@ mod write;
mod writebuffer;
mod writestream;
/// Errors that can be returned when trying to read from a buffer
/// Errors that can be returned when trying to read from or write to a buffer
#[derive(Debug, Error)]
pub enum BitError {
/// Too many bits requested to fit in the requested data type
@ -149,16 +170,20 @@ impl From<FromUtf8Error> for BitError {
}
}
/// Either the read bits in the requested format or a [`ReadError`](enum.ReadError.html)
/// Either the read bits in the requested format or a [`BitError`]
pub type Result<T> = std::result::Result<T, BitError>;
/// Get the number of bits required to read a type from stream
///
/// If the number of bits needed can not be determined beforehand `None` is returned
#[inline(always)]
pub fn bit_size_of<'a, T: BitRead<'a, LittleEndian>>() -> Option<usize> {
T::bit_size()
}
/// Get the number of bits required to read a type from stream
/// Get the number of bits required to read a type from stream given an input size
///
/// If the number of bits needed can not be determined beforehand `None` is returned
#[inline(always)]
pub fn bit_size_of_sized<'a, T: BitReadSized<'a, LittleEndian>>(size: usize) -> Option<usize> {
T::bit_size_sized(size)

View file

@ -87,9 +87,8 @@ use std::sync::Arc;
/// }
/// ```
///
/// [`BitReadSized`]: trait.BitReadSized.html
/// [read_sized]: struct.BitStream.html#method.read_sized
/// [read]: struct.BitStream.html#method.read
/// [read_sized]: BitReadStream::read_sized
/// [read]: BitReadStream::read
pub trait BitRead<'a, E: Endianness>: Sized {
/// Read the type from stream
fn read(stream: &mut BitReadStream<'a, E>) -> Result<Self>;
@ -454,9 +453,8 @@ impl<'a, E: Endianness, T: BitRead<'a, E>, const N: usize> BitRead<'a, E> for [T
/// }
/// ```
///
/// [`BitRead`]: trait.BitRead.html
/// [read_sized]: struct.BitStream.html#method.read_sized
/// [read]: struct.BitStream.html#method.read
/// [read_sized]: BitReadStream::read_sized
/// [read]: BitReadStream::read
pub trait BitReadSized<'a, E: Endianness>: Sized {
/// Read the type from stream
fn read(stream: &mut BitReadStream<'a, E>, size: usize) -> Result<Self>;

View file

@ -4,6 +4,81 @@ use std::rc::Rc;
use std::sync::Arc;
/// Trait for types that can be written to a stream without requiring the size to be configured
///
/// The `BitWrite` trait can be used with `#[derive]` on structs and enums
///
/// # Structs
///
/// The implementation can be derived for a struct as long as every field in the struct implements `BitWrite` or [`BitWriteSized`]
///
/// The struct is written field by field in the order they are defined in, if the size for a field is set [`stream.write_sized()`][write_sized]
/// will be used, otherwise [`write_read()`][write] will be used.
///
/// The size for a field can be set using 3 different methods
/// - set the size as an integer using the `size` attribute,
/// - use a previously defined field as the size using the `size` attribute
///
/// ## Examples
///
/// ```
/// # use bitbuffer::BitWrite;
/// #
/// #[derive(BitWrite)]
/// struct TestStruct {
/// foo: u8,
/// str: String,
/// #[size = 2] // when `size` is set, the attributed will be read using `read_sized`
/// truncated: String,
/// bar: u16,
/// float: f32,
/// #[size = 3]
/// asd: u8,
/// #[size = "asd"] // use a previously defined field as size
/// previous_field: u8,
/// }
/// ```
///
/// # Enums
///
/// The implementation can be derived for an enum as long as every variant of the enum either has no field, or an unnamed field that implements `BitWrite` or [`BitWriteSized`]
///
/// The enum is written by first writing a set number of bits as the discriminant of the enum, then the variant written.
///
/// For details about setting the input size for fields implementing [`BitWriteSized`] see the block about size in the `Structs` section above.
///
/// The discriminant for the variants defaults to incrementing by one for every field, starting with `0`.
/// You can overwrite the discriminant for a field, which will also change the discriminant for every following field.
///
/// ## Examples
///
/// ```
/// # use bitbuffer::BitWrite;
/// #
/// #[derive(BitWrite)]
/// #[discriminant_bits = 2]
/// enum TestBareEnum {
/// Foo,
/// Bar,
/// Asd = 3, // manually set the discriminant value for a field
/// }
/// ```
///
/// ```
/// # use bitbuffer::BitWrite;
/// #
/// #[derive(BitWrite)]
/// #[discriminant_bits = 2]
/// enum TestUnnamedFieldEnum {
/// #[size = 5]
/// Foo(i8),
/// Bar(bool),
/// #[discriminant = 3] // since rust only allows setting the discriminant on field-less enums, you can use an attribute instead
/// Asd(u8),
/// }
/// ```
///
/// [write_sized]: BitWriteStream::write_sized
/// [write]: BitWriteStream::write
pub trait BitWrite<E: Endianness> {
/// Write the type to stream
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()>;
@ -121,6 +196,67 @@ impl_write_tuple!(0: T1, 1: T2, 2: T3);
impl_write_tuple!(0: T1, 1: T2, 2: T3, 3: T4);
/// Trait for types that can be written to a stream, requiring the size to be configured
///
/// The meaning of the set sized depends on the type being written (e.g, number of bits for integers,
/// number of bytes for strings, number of items for Vec's, etc)
///
/// The `BitReadSized` trait can be used with `#[derive]` on structs
///
/// The implementation can be derived for a struct as long as every field in the struct implements [`BitWrite`] or `BitWriteSized`
///
/// The struct is written field by field in the order they are defined in, if the size for a field is set [`stream.write_sized()`][write_sized]
/// will be used, otherwise [`stream.write()`][write] will be used.
///
/// The size for a field can be set using 4 different methods
/// - set the size as an integer using the `size` attribute,
/// - use a previously defined field as the size using the `size` attribute
/// - based on the input size by setting `size` attribute to `"input_size"`
///
/// ## Examples
///
/// ```
/// # use bitbuffer::BitWriteSized;
/// #
/// #[derive(BitWriteSized, PartialEq, Debug)]
/// struct TestStructSized {
/// foo: u8,
/// #[size = "input_size"]
/// string: String,
/// #[size = "input_size"]
/// int: u8,
/// }
/// ```
///
/// # Enums
///
/// The implementation can be derived for an enum as long as every variant of the enum either has no field, or an unnamed field that implements [`BitWrite`] or `BitWriteSized`
///
/// The enum is written by first writing a set number of bits as the discriminant of the enum, then the variant is written.
///
/// For details about setting the input size for fields implementing `BitWriteSized` see the block about size in the `Structs` section above.
///
/// The discriminant for the variants defaults to incrementing by one for every field, starting with `0`.
/// You can overwrite the discriminant for a field, which will also change the discriminant for every following field.
///
/// ## Examples
///
/// ```
/// # use bitbuffer::BitWriteSized;
/// #
/// #[derive(BitWriteSized)]
/// #[discriminant_bits = 2]
/// enum TestUnnamedFieldEnum {
/// #[size = 5]
/// Foo(i8),
/// Bar(bool),
/// #[discriminant = 3] // since rust only allows setting the discriminant on field-less enums, you can use an attribute instead
/// #[size = "input_size"]
/// Asd(u8),
/// }
/// ```
///
/// [write_sized]: BitReadStream::write_sized
/// [write]: BitReadStream::write
pub trait BitWriteSized<E: Endianness> {
/// Write the type to stream
fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()>;