10 releases

0.1.0-rc.4 Nov 8, 2023
0.1.0-rc.2 Aug 22, 2023
0.1.0-rc.0 Jun 13, 2023
0.1.0-alpha.7 May 18, 2023
0.1.0-alpha.1 Aug 30, 2022

#995 in Procedural macros

39 downloads per month
Used in 2 crates (via flatty)

MIT/Apache

55KB
1.5K SLoC

flatty

Crates.io Docs.rs Gitlab CI License

Flat message buffers with direct mapping to Rust types without packing/unpacking.

Overview

Type called flat when it occupies a single contiguous memory area. Such types is useful when you need to store or send such object as a binary data while having convenient way to access its contents without serializing/deserializing.

This crate provides basic flat types and the way to create new user-defined composite flat types (using #[flat] attribute macro), which can be used almost like regular Rust structs or enums.

Also the crate can be used without std and even alloc.

Concepts

Conversion

Binary representation can be obtained from instances of flat type using as_bytes (and unsafe as_mut_bytes). Also bytes can be converted to flat types, see in-place initialization and validation.

DST

Flat type can be dynamically sized (like FlatVec), in that case it exploits Rust's ability to operate ?Sized types. User-defined flat struct can also be unsized (only last field is allowed to be unsized). Even flat enum can be unsized, but Rust doesn't natively support them yet so its contents could be accessed only via as_ref/as_mut methods returning a regular enum containing references to original enum contents.

In-place initialization

Sized types can be instantiated as usual Rust type. But in case of DST Rust cannot construct it in usual manner on stack because its size isn't known at compile time. Instead we may initialize such types onto given memory area.

To do this we can use so-called emplacer - something that can initialize object onto given memory. For sized types its instance is also emplacer.

Emplacer could be applied to raw bytes using new_in_place or replace existing struct contents using assign_in_place. Also some types has default emplacer and could be initialized in default state by default_in_place.

Validation

Not any combination of bytes are valid representation of flat type. For example, Bool has only two valid states: 0 and 1, or FlatVec length must not be greater than its capacity. validate can be used to check that data is valid for specific flat type, or from_bytes/from_mut_bytes also perform such check.

When you trust your data, then you can omit validation using unsafe from_bytes_unchecked/from_mut_bytes_unchecked, but this will cause an UB if data is invalid.

Portability

Flat type guarantee that it has the same binary representation on the platforms with same byte order, alignment and address width. If you need stronger guarantees you may use Portable types - they have the same binary representation on any platform and always aligned to byte. To make own flat type portable use #[flat(portable = true)]. Also this can be used to created packed flat types without alignment issues.

Examples

You can find some examples on how to create and use flat types in tests directory.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~1.5MB
~34K SLoC