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" license = "MIT"
description = "A binary serialization / deserialization strategy for transforming structs into bytes and vice versa!" 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 # 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 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. Both named and unnamed fields are serialized with their values only, and therefor encode to the same value.
```rs ```rs
#[derive(bincode::Serialize)] #[derive(bincode::Encodable)]
pub enum SomeEnum { pub enum SomeEnum {
A, A,
B(u32), B(u32),

View File

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

View File

@ -9,8 +9,6 @@ use read::Reader;
mod impls; mod impls;
pub mod read; pub mod read;
pub trait Decodable: Sized { pub trait Decodable: Sized {
fn decode<D: Decode>(decoder: D) -> Result<Self, Error>; 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> { impl<'storage> Reader<'storage> for SliceReader<'storage> {
#[inline(always)] #[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() { if bytes.len() > self.slice.len() {
return Err(Error::UnexpectedEnd); return Err(Error::UnexpectedEnd);
} }
@ -48,4 +48,4 @@ impl<'storage> Reader<'storage> for SliceReader<'storage> {
{ {
Ok(visitor(self.get_byte_slice(length)?)) Ok(visitor(self.get_byte_slice(length)?))
} }
} }

View File

@ -1,8 +1,20 @@
use super::{Encode, Encodeable}; use super::{Encode, Encodeable};
use crate::error::Error; 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 { impl Encodeable for u32 {
fn encode<E: Encode>(&self, mut encoder: E) -> Result<(), Error> { fn encode<E: Encode>(&self, mut encoder: E) -> Result<(), Error> {
encoder.encode_u32(*self) 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; mod impls;
pub mod write; pub mod write;
pub trait Encodeable { pub trait Encodeable {
fn encode<E: Encode>(&self, encoder: E) -> Result<(), Error>; fn encode<E: Encode>(&self, encoder: E) -> Result<(), Error>;
} }
pub trait Encode { pub trait Encode {
fn encode_u8(&mut self, val: u8) -> Result<(), Error>;
fn encode_u32(&mut self, val: u32) -> 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> { pub struct Encoder<W: Writer, C: Config> {
@ -30,9 +45,17 @@ impl<W: Writer, C: Config> Encoder<W, C> {
config: PhantomData, config: PhantomData,
} }
} }
pub fn into_writer(self) -> W {
self.writer
}
} }
impl<'a, W: Writer, C: Config> Encode for &'a mut Encoder<W, C> { 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> { fn encode_u32(&mut self, val: u32) -> Result<(), Error> {
let bytes = match C::ENDIAN { let bytes = match C::ENDIAN {
Endian::Little => val.to_le_bytes(), 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) 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> { pub struct SliceWriter<'storage> {
slice: &'storage mut [u8], slice: &'storage mut [u8],
idx: usize,
} }
impl<'storage> SliceWriter<'storage> { impl<'storage> SliceWriter<'storage> {
pub(crate) fn new(bytes: &'storage mut [u8]) -> SliceWriter<'storage> { pub(crate) fn new(bytes: &'storage mut [u8]) -> SliceWriter<'storage> {
SliceWriter { SliceWriter {
slice: bytes, slice: bytes,
idx: 0,
} }
} }
pub(crate) fn bytes_written(&self) -> usize {
self.idx
}
} }
impl<'storage> Writer for SliceWriter<'storage> { impl<'storage> Writer for SliceWriter<'storage> {
fn write(&mut self, bytes: &[u8]) -> Result<(), Error> { 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); return Err(Error::UnexpectedEnd);
} }
let data = core::mem::take(&mut self.slice); self.idx += bytes.len();
let (write_slice, remaining) = data.split_at_mut(bytes.len()); let write_slice = &mut remaining[..bytes.len()];
write_slice.copy_from_slice(bytes); write_slice.copy_from_slice(bytes);
self.slice = remaining;
Ok(()) Ok(())
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{error::Error, enc::write::Writer}; use crate::{enc::write::Writer, error::Error};
pub trait IntEncoding { pub trait IntEncoding {
fn encode_u32<W: Writer>(writer: &mut W, val: u32) -> Result<(), Error>; 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 config;
pub mod de; pub mod de;
pub mod error;
pub mod enc; pub mod enc;
pub mod error;
pub use bincode_derive::{Decodable, Encodable};
pub(crate) mod int_encoding; 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 writer = enc::write::SliceWriter::new(dst);
let mut encoder = enc::Encoder::<_, config::Default>::new(writer); 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> { 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]);
}