Started working on bincode_derive

This commit is contained in:
Victor Koenders 2021-09-13 13:20:47 +02:00
parent a6435388a1
commit bab0cf4bd1
18 changed files with 315 additions and 24 deletions

View File

@ -15,4 +15,13 @@ keywords = ["binary", "encode", "decode", "serialize", "deserialize"]
license = "MIT"
description = "A binary serialization / deserialization strategy for transforming structs into bytes and vice versa!"
edition = "2018"
edition = "2018"
[features]
default = ["std"]
std = []
alloc = []
[dependencies]
bincode_derive = { path = "derive" }
# serde = { version = "1.0.130", optional = true }

1
derive/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

46
derive/Cargo.lock generated Normal file
View File

@ -0,0 +1,46 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bincode_derive"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

15
derive/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "bincode_derive"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "1.0.9"
[dependencies.syn]
version = "1.0.74"
default-features = false
features = ["parsing", "derive", "proc-macro", "printing"]

19
derive/src/derive_enum.rs Normal file
View File

@ -0,0 +1,19 @@
use crate::Result;
use proc_macro::TokenStream;
use syn::Ident;
pub struct DeriveEnum {}
impl DeriveEnum {
pub fn parse(_name: Ident, _en: syn::DataEnum) -> Result<Self> {
unimplemented!()
}
pub fn to_encodable(self) -> Result<TokenStream> {
unimplemented!()
}
pub fn to_decodable(self) -> Result<TokenStream> {
unimplemented!()
}
}

View File

@ -0,0 +1,57 @@
use crate::Result;
use proc_macro::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Ident};
pub struct DeriveStruct {
name: Ident,
fields: Vec<Ident>,
}
impl DeriveStruct {
pub fn parse(name: Ident, str: syn::DataStruct) -> Result<Self> {
let fields = match str.fields {
syn::Fields::Named(fields) => fields
.named
.iter()
.map(|f| f.ident.clone().unwrap())
.collect(),
syn::Fields::Unnamed(fields) => fields
.unnamed
.iter()
.enumerate()
.map(|(i, field)| Ident::new(&i.to_string(), field.ty.span()))
.collect(),
syn::Fields::Unit => Vec::new(),
};
Ok(Self { name, fields })
}
pub fn to_encodable(self) -> Result<TokenStream> {
let DeriveStruct { name, fields } = self;
let fields = fields
.into_iter()
.map(|field| {
quote! {
bincode::enc::Encodeable::encode(&self. #field, &mut encoder)?;
}
})
.collect::<Vec<_>>();
let result = quote! {
impl bincode::enc::Encodeable for #name {
fn encode<E: bincode::enc::Encode>(&self, mut encoder: E) -> Result<(), bincode::error::Error> {
#(#fields)*
Ok(())
}
}
};
Ok(result.into())
}
pub fn to_decodable(self) -> Result<TokenStream> {
unimplemented!()
}
}

24
derive/src/error.rs Normal file
View File

@ -0,0 +1,24 @@
use proc_macro::TokenStream;
use quote::__private::Span;
use std::fmt;
pub enum Error {
UnionNotSupported,
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::UnionNotSupported => write!(fmt, "Unions are not supported"),
}
}
}
impl Error {
pub fn into_token_stream(self) -> TokenStream {
self.into_token_stream_with_span(Span::call_site())
}
pub fn into_token_stream_with_span(self, span: Span) -> TokenStream {
syn::Error::new(span, self).into_compile_error().into()
}
}

49
derive/src/lib.rs Normal file
View File

@ -0,0 +1,49 @@
extern crate proc_macro;
mod derive_enum;
mod derive_struct;
mod error;
use derive_enum::DeriveEnum;
use derive_struct::DeriveStruct;
use error::Error;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
type Result<T = ()> = std::result::Result<T, Error>;
#[proc_macro_derive(Encodable)]
pub fn derive_encodable(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_encodable_inner(input).unwrap_or_else(|e| e.into_token_stream())
}
fn derive_encodable_inner(input: DeriveInput) -> Result<TokenStream> {
match input.data {
syn::Data::Struct(struct_definition) => {
DeriveStruct::parse(input.ident, struct_definition).and_then(|str| str.to_encodable())
}
syn::Data::Enum(enum_definition) => {
DeriveEnum::parse(input.ident, enum_definition).and_then(|str| str.to_encodable())
}
syn::Data::Union(_) => Err(Error::UnionNotSupported),
}
}
#[proc_macro_derive(Decodable)]
pub fn derive_decodable(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_decodable_inner(input).unwrap_or_else(|e| e.into_token_stream())
}
fn derive_decodable_inner(input: DeriveInput) -> Result<TokenStream> {
match input.data {
syn::Data::Struct(struct_definition) => {
DeriveStruct::parse(input.ident, struct_definition).and_then(|str| str.to_decodable())
}
syn::Data::Enum(enum_definition) => {
DeriveEnum::parse(input.ident, enum_definition).and_then(|str| str.to_decodable())
}
syn::Data::Union(_) => Err(Error::UnionNotSupported),
}
}

View File

@ -1,6 +1,6 @@
# Serialization specification
*NOTE*: Serialization is done by `bincode_derive` by default. If you enable the `serde` flag, serialization is done by `serde-derive` instead. `serde-derive` has the same guarantees as `bincode_derive` for now.
*NOTE*: Serialization is done by `bincode_derive` by default. If you enable the `serde` flag, serialization with `serde-derive` is supported as well. `serde-derive` has the same guarantees as `bincode_derive` for now.
Related issue: https://github.com/serde-rs/serde/issues/1756#issuecomment-689682123
@ -51,7 +51,7 @@ Enums are encoded with their variant first, followed by optionally the variant f
Both named and unnamed fields are serialized with their values only, and therefor encode to the same value.
```rs
#[derive(bincode::Serialize)]
#[derive(bincode::Encodable)]
pub enum SomeEnum {
A,
B(u32),

View File

@ -1,8 +1,8 @@
use super::{Decode, Decodable};
use super::{Decodable, Decode};
use crate::error::Error;
impl Decodable for u32 {
fn decode<D: Decode>(mut decoder: D) -> Result<Self, Error> {
decoder.decode_u32()
}
}
}

View File

@ -9,8 +9,6 @@ use read::Reader;
mod impls;
pub mod read;
pub trait Decodable: Sized {
fn decode<D: Decode>(decoder: D) -> Result<Self, Error>;
}
@ -44,5 +42,3 @@ impl<'a, 'de, R: Reader<'de>, C: Config> Decode for &'a mut Decoder<R, C> {
})
}
}

View File

@ -30,7 +30,7 @@ impl<'storage> SliceReader<'storage> {
impl<'storage> Reader<'storage> for SliceReader<'storage> {
#[inline(always)]
fn read<'a>(&'a mut self, bytes: &mut [u8]) -> Result<(), Error> {
fn read(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
if bytes.len() > self.slice.len() {
return Err(Error::UnexpectedEnd);
}
@ -48,4 +48,4 @@ impl<'storage> Reader<'storage> for SliceReader<'storage> {
{
Ok(visitor(self.get_byte_slice(length)?))
}
}
}

View File

@ -1,8 +1,20 @@
use super::{Encode, Encodeable};
use crate::error::Error;
impl Encodeable for u8 {
fn encode<E: Encode>(&self, mut encoder: E) -> Result<(), Error> {
encoder.encode_u8(*self)
}
}
impl Encodeable for u32 {
fn encode<E: Encode>(&self, mut encoder: E) -> Result<(), Error> {
encoder.encode_u32(*self)
}
}
}
impl Encodeable for i32 {
fn encode<E: Encode>(&self, mut encoder: E) -> Result<(), Error> {
encoder.encode_i32(*self)
}
}

View File

@ -9,13 +9,28 @@ use write::Writer;
mod impls;
pub mod write;
pub trait Encodeable {
fn encode<E: Encode>(&self, encoder: E) -> Result<(), Error>;
}
pub trait Encode {
fn encode_u8(&mut self, val: u8) -> Result<(), Error>;
fn encode_u32(&mut self, val: u32) -> Result<(), Error>;
fn encode_i32(&mut self, val: i32) -> Result<(), Error>;
}
impl<'a, T> Encode for &'a mut T
where
T: Encode,
{
fn encode_u8(&mut self, val: u8) -> Result<(), Error> {
T::encode_u8(self, val)
}
fn encode_u32(&mut self, val: u32) -> Result<(), Error> {
T::encode_u32(self, val)
}
fn encode_i32(&mut self, val: i32) -> Result<(), Error> {
T::encode_i32(self, val)
}
}
pub struct Encoder<W: Writer, C: Config> {
@ -30,9 +45,17 @@ impl<W: Writer, C: Config> Encoder<W, C> {
config: PhantomData,
}
}
pub fn into_writer(self) -> W {
self.writer
}
}
impl<'a, W: Writer, C: Config> Encode for &'a mut Encoder<W, C> {
fn encode_u8(&mut self, val: u8) -> Result<(), Error> {
self.writer.write(&[val])
}
fn encode_u32(&mut self, val: u32) -> Result<(), Error> {
let bytes = match C::ENDIAN {
Endian::Little => val.to_le_bytes(),
@ -41,4 +64,13 @@ impl<'a, W: Writer, C: Config> Encode for &'a mut Encoder<W, C> {
self.writer.write(&bytes)
}
}
fn encode_i32(&mut self, val: i32) -> Result<(), Error> {
let bytes = match C::ENDIAN {
Endian::Little => val.to_le_bytes(),
Endian::Big => val.to_be_bytes(),
};
self.writer.write(&bytes)
}
}

View File

@ -6,25 +6,31 @@ pub trait Writer {
pub struct SliceWriter<'storage> {
slice: &'storage mut [u8],
idx: usize,
}
impl<'storage> SliceWriter<'storage> {
pub(crate) fn new(bytes: &'storage mut [u8]) -> SliceWriter<'storage> {
SliceWriter {
slice: bytes,
idx: 0,
}
}
pub(crate) fn bytes_written(&self) -> usize {
self.idx
}
}
impl<'storage> Writer for SliceWriter<'storage> {
fn write(&mut self, bytes: &[u8]) -> Result<(), Error> {
if bytes.len() > self.slice.len() {
let remaining = &mut self.slice[self.idx..];
if bytes.len() > remaining.len() {
return Err(Error::UnexpectedEnd);
}
let data = core::mem::take(&mut self.slice);
let (write_slice, remaining) = data.split_at_mut(bytes.len());
self.idx += bytes.len();
let write_slice = &mut remaining[..bytes.len()];
write_slice.copy_from_slice(bytes);
self.slice = remaining;
Ok(())
}
}
}

View File

@ -1,4 +1,4 @@
use crate::{error::Error, enc::write::Writer};
use crate::{enc::write::Writer, error::Error};
pub trait IntEncoding {
fn encode_u32<W: Writer>(writer: &mut W, val: u32) -> Result<(), Error>;

View File

@ -17,15 +17,21 @@ extern crate std;
pub mod config;
pub mod de;
pub mod error;
pub mod enc;
pub mod error;
pub use bincode_derive::{Decodable, Encodable};
pub(crate) mod int_encoding;
pub fn encode_into_slice<E: enc::Encodeable>(val: E, dst: &mut [u8]) -> Result<(), error::Error> {
pub fn encode_into_slice<E: enc::Encodeable>(
val: E,
dst: &mut [u8],
) -> Result<usize, error::Error> {
let writer = enc::write::SliceWriter::new(dst);
let mut encoder = enc::Encoder::<_, config::Default>::new(writer);
val.encode(&mut encoder)
val.encode(&mut encoder)?;
Ok(encoder.into_writer().bytes_written())
}
pub fn decode<D: de::Decodable>(src: &mut [u8]) -> Result<D, error::Error> {

19
tests/derive.rs Normal file
View File

@ -0,0 +1,19 @@
#[derive(bincode::Encodable, PartialEq, Debug)]
pub struct Test {
a: i32,
b: u32,
c: u8,
}
#[test]
fn test_encodable() {
let start = Test {
a: 5i32,
b: 10u32,
c: 20u8,
};
let mut slice = [0u8; 1024];
let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap();
assert_eq!(bytes_written, 9);
assert_eq!(&slice[..bytes_written], &[5, 0, 0, 0, 10, 0, 0, 0, 20]);
}