summaryrefslogtreecommitdiff
path: root/bzipper/src/lib.rs
blob: dd64693676f6421c1c57205067c47bb811f1398a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2024 Gabriel Bjørnager Jensen.
//
// This file is part of bzipper.
//
// bzipper is free software: you can redistribute
// it and/or modify it under the terms of the GNU
// Lesser General Public License as published by
// the Free Software Foundation, either version 3
// of the License, or (at your option) any later
// version.
//
// bzipper is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Less-
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.

#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg?ref_type=heads")]

//! Binary (de)serialisation.
//!
//! Contrary to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the goal of bzipper is to serialise with a known size constraint.
//! Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.
//!
//! Keep in mind that this project is still work-in-progress.
//!
//! This crate is compatible with `no_std`.
//!
//! # Data model
//!
//! Most primitive types serialise losslessly, with the exception being [`usize`] and [`isize`].
//! These serialise as [`u32`] and [`i32`], respectively, for portability reasons.
//!
//! Unsized types, such as [`str`] and [slices](slice), are not supported.
//! Instead, [arrays](array) should be used.
//! For strings, the [`FixedString`] type is also provided.
//!
//! # Usage
//!
//! This crate revolves around the [`Serialise`] and [`Deserialise`] traits, both of which are commonly used in conjunction with streams (more specifically, [s-streams](Sstream) and [d-streams](Dstream)).
//!
//! Many core types come implemented with bzipper, including primitives as well as some standard library types such as [`Option`] and [`Result`](core::result::Result).
//!
//! It is recommended in most cases to just derive these traits for custom types (enumerations and structures only).
//! Here, each field is chained in declaration order:
//!
//! ```
//! use bzipper::{Deserialise, Serialise};
//!
//! #[derive(Debug, Deserialise, PartialEq, Serialise)]
//! struct IoRegister {
//!     addr:  u32,
//!     value: u16,
//! }
//!
//! let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
//! IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
//!
//! assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
//!
//! assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
//! ```
//!
//! ## Serialisation
//!
//! To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation.
//! The required size of any given serialisation is specified by the [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) constant:
//!
//! ```
//! use bzipper::Serialise;
//!
//! let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
//! 'Ж'.serialise(&mut buf).unwrap();
//!
//! assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
//! ```
//!
//! The only special requirement of the [`serialise`](Serialise::serialise) method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
//!
//! We can also use streams to *chain* multiple elements together.
//!
//! ```
//! use bzipper::Serialise;
//!
//! let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
//! let mut stream = bzipper::Sstream::new(&mut buf);
//!
//! stream.append(&'ل');
//! stream.append(&'ا');
//! stream.append(&'م');
//! stream.append(&'د');
//! stream.append(&'ا');
//!
//! assert_eq!(buf, [0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F, 0x00, 0x00, 0x06, 0x27]);
//! ```
//!
//! When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian).
//! It is recommended for implementors to adhere to this convention as well.
//!
//! ## Deserialisation
//!
//! Deserialisation works with an almost identical syntax to serialisation.
//!
//! To deserialise a buffer, simply call the [`deserialise`](Deserialise::deserialise) method:
//!
//! ```
//! use bzipper::Deserialise;
//!
//! let data = [0x45, 0x54];
//! assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
//! ```
//!
//! Just like with serialisations, the [`Dstream`] can be used to deserialise chained elements:
//!
//! ```
//! use bzipper::Deserialise;
//!
//! let data = [0x45, 0x54];
//! let stream = bzipper::Dstream::new(&data);
//!
//! assert_eq!(stream.take::<u8>().unwrap(), 0x45);
//! assert_eq!(stream.take::<u8>().unwrap(), 0x54);
//! ```

#![no_std]

#![cfg_attr(doc, feature(doc_cfg))]

extern crate self as bzipper;

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(feature = "alloc")]
extern crate std;

/// Implements [`Deserialise`] for the provided type.
///
/// This macro assumes that `Serialise` was also derived, although this is not strictly required as it is unenforceable.
#[doc(inline)]
pub use bzipper_macros::Deserialise;

/// Implements [`Serialise`] for the provided type.
///
/// # Structs
///
/// For structures, each element is chained in **order of declaration.**
/// For example, the following struct will serialise its field `foo` before `bar`:
///
/// ```
/// use bzipper::Serialise;
///
/// #[derive(Serialise)]
/// pub struct FooBar {
///     pub foo: char,
///     pub bar: char,
/// }
/// ```
///
/// Should the order of declaration change, then most of---if not all---previous dervied serialisations become void.
///
/// The value of [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) is set to the combined value of all fields.
///
/// If the structure is a unit structure (i.e. it has *no* fields), it is serialised equivalently to the [unit] type.
///
/// # Enums
///
/// Enumerations are serialised by first assigning each variant its own discriminant.
/// By default, each discriminant is assigned from the range 0 to infinite, to the extend allowed by the `u32` type (as which the discriminant is encoded).
/// In the future, however, custom representations and assigned discriminants will be honoured.
///
/// Variants with fields are serialised exactly like structures.
/// That is, each field is chained in order of declaration.
///
/// Each variant has its own serialised size, and the largest of these values is chosen as the serialised size of the enumeration type.
///
/// # Unions
///
/// Unions cannot derive `Serialise` due to the uncertainty of their contents.
/// The trait should therefore be implemented manually.
#[doc(inline)]
pub use bzipper_macros::Serialise;

macro_rules! use_mod {
	($vis:vis $name:ident) => {
		mod $name;
		$vis use $name::*;
	};
}
pub(in crate) use use_mod;

use_mod!(pub deserialise);
use_mod!(pub dstream);
use_mod!(pub error);
use_mod!(pub fixed_iter);
use_mod!(pub fixed_string);
use_mod!(pub serialise);
use_mod!(pub sstream);

#[cfg(feature = "alloc")]
use_mod!(pub buffer);