# Bitsong
Fast `#[no_std]` macro-based serialization/deserialization into a minimal binary format. Makes no memory allocations, doesn't use tagging (except for enum discriminants), and supports enums and enums with tuple variants. Doesn't support strings (but you can use a buffer) or struct enum variants.
## Getting started
- Use `#[derive(SongSize, FromSong, ToSong)]` on your struct/enum (or implement the traits manually)
- Check how many bytes will be written with `my_struct.song_size()`
- Use `my_struct.to_song(&mut buf)` to write `my_struct` into a buffer `buf`
- Use `MyStruct::from_song(&buf)` to read type `MyStruct` from a buffer `buf`
## How it works
The derive macros generate the serializers and deserializers. The save format is dead simple; for structs, it is every field in the order they appear (without any tagging bytes), and for enums there is one discriminant field and then any associated enum data. It is very similar to what you would get with `mem::transmute`, except without any padding bytes and with the additional guarantee that it'll always work cross-platform.
This code has been used by Case Rocket Team and seems to work well enough, though some of the macro implementation is a bit messy. We use it for reading and writing low bandwidth LoRa radio packets.
## Example code
```rust
use crate::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, FromSong, ToSong, SongSize)]
#[repr(u8)]
enum Color {
Unknown = 0x00,
Red = 0x01,
Green = 0x02,
Blue = 0x03
}
#[derive(Clone, Debug, PartialEq, Eq, FromSong, ToSong, SongSize)]
struct Cat {
fluffy_paws: bool
}
#[derive(Clone, Debug, PartialEq, Eq, FromSong, ToSong, SongSize)]
#[song(discriminant(PetType = u8))]
enum Pet {
Dog,
Cat(Cat),
Fish(Color)
}
#[derive(Clone, Debug, PartialEq, Eq, FromSong, ToSong, SongSize)]
struct Person {
name: [u8; 16],
age: u8,
favorite_color: Color,
pet: Option<Pet>
}
fn new_joe() -> Person {
let mut name = [0; 16];
name[0..3].copy_from_slice(b"Joe");
Person {
name,
age: 21,
favorite_color: Color::Green,
pet: Some(Pet::Cat(Cat {
fluffy_paws: true
}))
}
}
const JOE_SONG: [u8; 21] = [74, 111, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 2, 1, 1, 1];
#[test]
fn serialize() {
let mut buf = [0; 21];
new_joe().to_song(&mut buf).unwrap();
assert_eq!(buf, JOE_SONG);
}
#[test]
fn deserialize() {
let joe = Person::from_song(&JOE_SONG).unwrap();
assert_eq!(joe, new_joe())
}
```
## Running no_panic tests
```sh
cargo test --profile release --features no_panic
```