Merge pull request #980 from ethcore/binary-serializer
Auto (with codegen) binary serializer
This commit is contained in:
		
						commit
						2d6f9af612
					
				
							
								
								
									
										8
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -237,11 +237,15 @@ dependencies = [ | |||||||
| name = "ethcore" | name = "ethcore" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "bincode 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)", |  "clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", |  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethash 1.1.0", |  "ethash 1.1.0", | ||||||
|  "ethcore-devtools 1.1.0", |  "ethcore-devtools 1.1.0", | ||||||
|  |  "ethcore-ipc 1.1.0", | ||||||
|  |  "ethcore-ipc-codegen 1.1.0", | ||||||
|  |  "ethcore-ipc-nano 1.1.0", | ||||||
|  "ethcore-util 1.1.0", |  "ethcore-util 1.1.0", | ||||||
|  "ethjson 0.1.0", |  "ethjson 0.1.0", | ||||||
|  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", |  "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -250,6 +254,10 @@ dependencies = [ | |||||||
|  "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", |  "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rust-crypto 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", |  "rust-crypto 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", |  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "serde_codegen 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "syntex 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", |  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -298,14 +298,14 @@ fn implement_dispatch_arms( | |||||||
| 		.map(|dispatch| { index = index + 1; implement_dispatch_arm(cx, builder, index as u32, dispatch, buffer, replacements) }).collect() | 		.map(|dispatch| { index = index + 1; implement_dispatch_arm(cx, builder, index as u32, dispatch, buffer, replacements) }).collect() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn strip_ptr(ty: &P<ast::Ty>) -> P<ast::Ty> { | pub fn strip_ptr(ty: &P<ast::Ty>) -> P<ast::Ty> { | ||||||
| 	if let ast::TyKind::Rptr(_, ref ptr_mut) = ty.node { | 	if let ast::TyKind::Rptr(_, ref ptr_mut) = ty.node { | ||||||
| 		ptr_mut.ty.clone() | 		ptr_mut.ty.clone() | ||||||
| 	} | 	} | ||||||
| 	else { ty.clone() } | 	else { ty.clone() } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn has_ptr(ty: &P<ast::Ty>) -> bool { | pub fn has_ptr(ty: &P<ast::Ty>) -> bool { | ||||||
| 	if let ast::TyKind::Rptr(_, ref _ptr_mut) = ty.node { | 	if let ast::TyKind::Rptr(_, ref _ptr_mut) = ty.node { | ||||||
| 		true | 		true | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ pub fn register(reg: &mut syntex::Registry) { | |||||||
| 	reg.add_attr("feature(custom_attribute)"); | 	reg.add_attr("feature(custom_attribute)"); | ||||||
| 
 | 
 | ||||||
| 	reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation); | 	reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation); | ||||||
|  | 	reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(not(feature = "with-syntex"))] | #[cfg(not(feature = "with-syntex"))] | ||||||
| @ -62,4 +63,8 @@ pub fn register(reg: &mut rustc_plugin::Registry) { | |||||||
| 		syntax::parse::token::intern("derive_Ipc"), | 		syntax::parse::token::intern("derive_Ipc"), | ||||||
| 		syntax::ext::base::MultiDecorator( | 		syntax::ext::base::MultiDecorator( | ||||||
| 			Box::new(codegen::expand_ipc_implementation))); | 			Box::new(codegen::expand_ipc_implementation))); | ||||||
|  | 	reg.register_syntax_extension( | ||||||
|  | 		syntax::parse::token::intern("derive_Binary"), | ||||||
|  | 		syntax::ext::base::MultiDecorator( | ||||||
|  | 			Box::new(serialization::expand_serialization_implementation))); | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,4 +15,5 @@ | |||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| mod codegen; | mod codegen; | ||||||
|  | mod serialization; | ||||||
| pub mod typegen; | pub mod typegen; | ||||||
|  | |||||||
							
								
								
									
										585
									
								
								ipc/codegen/src/serialization.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										585
									
								
								ipc/codegen/src/serialization.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,585 @@ | |||||||
|  | // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity 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 General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | use aster; | ||||||
|  | 
 | ||||||
|  | use syntax::ast::{ | ||||||
|  | 	MetaItem, | ||||||
|  | 	Item, | ||||||
|  | 	Ident, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use syntax::ast; | ||||||
|  | use syntax::codemap::Span; | ||||||
|  | use syntax::ext::base::{Annotatable, ExtCtxt}; | ||||||
|  | use syntax::ext::build::AstBuilder; | ||||||
|  | use syntax::ptr::P; | ||||||
|  | 
 | ||||||
|  | pub struct Error; | ||||||
|  | 
 | ||||||
|  | use super::codegen; | ||||||
|  | 
 | ||||||
|  | pub fn expand_serialization_implementation( | ||||||
|  | 	cx: &mut ExtCtxt, | ||||||
|  | 	span: Span, | ||||||
|  | 	meta_item: &MetaItem, | ||||||
|  | 	annotatable: &Annotatable, | ||||||
|  | 	push: &mut FnMut(Annotatable) | ||||||
|  | ) { | ||||||
|  | 	let item = match *annotatable { | ||||||
|  | 		Annotatable::Item(ref item) => item, | ||||||
|  | 		_ => { | ||||||
|  | 			cx.span_err(meta_item.span, "`#[derive(Binary)]` may only be applied to structs and enums"); | ||||||
|  | 			return; | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	let builder = aster::AstBuilder::new().span(span); | ||||||
|  | 
 | ||||||
|  | 	let impl_item = match serialize_item(cx, &builder, &item) { | ||||||
|  | 		Ok(item) => item, | ||||||
|  | 		Err(Error) => { | ||||||
|  | 			// An error occured, but it should have been reported already.
 | ||||||
|  | 			return; | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	push(Annotatable::Item(impl_item)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn serialize_item( | ||||||
|  | 	cx: &ExtCtxt, | ||||||
|  | 	builder: &aster::AstBuilder, | ||||||
|  | 	item: &Item, | ||||||
|  | ) -> Result<P<ast::Item>, Error> { | ||||||
|  | 	let generics = match item.node { | ||||||
|  | 		ast::ItemKind::Struct(_, ref generics) => generics, | ||||||
|  | 		ast::ItemKind::Enum(_, ref generics) => generics, | ||||||
|  | 		_ => { | ||||||
|  | 			cx.span_err( | ||||||
|  | 				item.span, | ||||||
|  | 				"`#[derive(Binary)]` may only be applied to structs and enums"); | ||||||
|  | 			return Err(Error); | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	let ty = builder.ty().path() | ||||||
|  | 		.segment(item.ident).with_generics(generics.clone()).build() | ||||||
|  | 		.build(); | ||||||
|  | 
 | ||||||
|  | 	let where_clause = &generics.where_clause; | ||||||
|  | 
 | ||||||
|  | 	let binary_expressions = try!(binary_expr(cx, | ||||||
|  | 		&builder, | ||||||
|  | 		&item, | ||||||
|  | 		&generics, | ||||||
|  | 		ty.clone())); | ||||||
|  | 
 | ||||||
|  | 	let (size_expr, read_expr, write_expr) = | ||||||
|  | 		(binary_expressions.size, binary_expressions.read, binary_expressions.write); | ||||||
|  | 
 | ||||||
|  | 	Ok(quote_item!(cx, | ||||||
|  | 		impl $generics ::ipc::BinaryConvertable for $ty $where_clause { | ||||||
|  | 			fn size(&self) -> usize { | ||||||
|  | 				$size_expr | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> { | ||||||
|  | 				$write_expr | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> { | ||||||
|  | 				$read_expr | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			fn len_params() -> usize { | ||||||
|  | 				1 | ||||||
|  | 			} | ||||||
|  |         } | ||||||
|  |     ).unwrap()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[allow(unreachable_code)] | ||||||
|  | fn binary_expr( | ||||||
|  | 	cx: &ExtCtxt, | ||||||
|  | 	builder: &aster::AstBuilder, | ||||||
|  | 	item: &Item, | ||||||
|  | 	impl_generics: &ast::Generics, | ||||||
|  | 	ty: P<ast::Ty>, | ||||||
|  | ) -> Result<BinaryExpressions, Error> { | ||||||
|  | 	match item.node { | ||||||
|  | 		ast::ItemKind::Struct(ref variant_data, _) => { | ||||||
|  | 			binary_expr_item_struct( | ||||||
|  | 				cx, | ||||||
|  | 				builder, | ||||||
|  | 				impl_generics, | ||||||
|  | 				ty, | ||||||
|  | 				item.span, | ||||||
|  | 				variant_data, | ||||||
|  | 			) | ||||||
|  | 		}, | ||||||
|  | 		ast::ItemKind::Enum(ref enum_def, _) => { | ||||||
|  | 			binary_expr_enum( | ||||||
|  | 				cx, | ||||||
|  | 				builder, | ||||||
|  | 				item.ident, | ||||||
|  | 				impl_generics, | ||||||
|  | 				ty, | ||||||
|  | 				item.span, | ||||||
|  | 				enum_def, | ||||||
|  | 			) | ||||||
|  | 		}, | ||||||
|  | 		_ => { | ||||||
|  | 			cx.span_bug(item.span, | ||||||
|  | 						"expected ItemStruct or ItemEnum in #[derive(Binary)]"); | ||||||
|  | 			Err(Error) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct BinaryExpressions { | ||||||
|  | 	pub size: P<ast::Expr>, | ||||||
|  | 	pub write: P<ast::Expr>, | ||||||
|  | 	pub read: P<ast::Expr>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn binary_expr_struct( | ||||||
|  | 	cx: &ExtCtxt, | ||||||
|  | 	builder: &aster::AstBuilder, | ||||||
|  | 	ty: P<ast::Ty>, | ||||||
|  | 	fields: &[ast::StructField], | ||||||
|  | 	value_ident: Option<ast::Ident>, | ||||||
|  | 	instance_ident: Option<ast::Ident>, | ||||||
|  | ) -> Result<BinaryExpressions, Error> { | ||||||
|  | 
 | ||||||
|  | 	let size_exprs: Vec<P<ast::Expr>> = fields.iter().enumerate().map(|(index, field)| { | ||||||
|  | 		let field_type_ident = builder.id( | ||||||
|  | 			&::syntax::print::pprust::ty_to_string( | ||||||
|  | 				&codegen::strip_ptr(&field.ty))); | ||||||
|  | 		let index_ident = builder.id(format!("__field{}", index)); | ||||||
|  | 		value_ident.and_then(|x| { | ||||||
|  | 				let field_id = builder.id(field.ident.unwrap()); | ||||||
|  | 				Some(quote_expr!(cx, | ||||||
|  | 					match $field_type_ident::len_params() { | ||||||
|  | 						0 => mem::size_of::<$field_type_ident>(), | ||||||
|  | 						_ => $x. $field_id .size(), | ||||||
|  | 					})) | ||||||
|  | 			}) | ||||||
|  | 			.unwrap_or_else(|| quote_expr!(cx, match $field_type_ident::len_params() { | ||||||
|  | 				0 => mem::size_of::<$field_type_ident>(), | ||||||
|  | 				_ => $index_ident .size(), | ||||||
|  | 			})) | ||||||
|  | 	}).collect(); | ||||||
|  | 
 | ||||||
|  | 	let first_size_expr = size_exprs[0].clone(); | ||||||
|  | 	let mut total_size_expr = quote_expr!(cx, 0usize + $first_size_expr); | ||||||
|  | 	for index in 1..size_exprs.len() { | ||||||
|  | 		let next_expr = size_exprs[index].clone(); | ||||||
|  | 		total_size_expr = quote_expr!(cx, $total_size_expr + $next_expr); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	let mut write_stmts = Vec::<ast::Stmt>::new(); | ||||||
|  | 	write_stmts.push(quote_stmt!(cx, let mut offset = 0usize;).unwrap()); | ||||||
|  | 
 | ||||||
|  | 	let mut map_stmts = Vec::<ast::Stmt>::new(); | ||||||
|  | 	let field_amount = builder.id(&format!("{}",fields.len())); | ||||||
|  | 	map_stmts.push(quote_stmt!(cx, let mut map = vec![0usize; $field_amount];).unwrap()); | ||||||
|  | 	map_stmts.push(quote_stmt!(cx, let mut total = 0usize;).unwrap()); | ||||||
|  | 	for (index, field) in fields.iter().enumerate() { | ||||||
|  | 		let field_type_ident = builder.id( | ||||||
|  | 			&::syntax::print::pprust::ty_to_string( | ||||||
|  | 				&codegen::strip_ptr(&field.ty))); | ||||||
|  | 
 | ||||||
|  | 		let member_expr = match value_ident { | ||||||
|  | 			Some(x) => { | ||||||
|  | 				let field_id = builder.id(field.ident.unwrap()); | ||||||
|  | 				quote_expr!(cx, $x . $field_id) | ||||||
|  | 			}, | ||||||
|  | 			None => { | ||||||
|  | 				let index_ident = builder.id(format!("__field{}", index)); | ||||||
|  | 				quote_expr!(cx, $index_ident) | ||||||
|  | 			}, | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident::len_params() { | ||||||
|  | 				0 => mem::size_of::<$field_type_ident>(), | ||||||
|  | 				_ => { let size = $member_expr .size(); length_stack.push_back(size); size }, | ||||||
|  | 			}).unwrap()); | ||||||
|  | 
 | ||||||
|  | 		write_stmts.push(quote_stmt!(cx, | ||||||
|  | 			if let Err(e) = $member_expr .to_bytes(&mut buffer[offset..next_line], length_stack) { return Err(e) };).unwrap()); | ||||||
|  | 
 | ||||||
|  | 		write_stmts.push(quote_stmt!(cx, offset = next_line; ).unwrap()); | ||||||
|  | 
 | ||||||
|  | 		let field_index = builder.id(&format!("{}", index)); | ||||||
|  | 		map_stmts.push(quote_stmt!(cx, map[$field_index] = total;).unwrap()); | ||||||
|  | 		map_stmts.push(quote_stmt!(cx, let size = match $field_type_ident::len_params() { | ||||||
|  | 				0 => mem::size_of::<$field_type_ident>(), | ||||||
|  | 				_ => length_stack.pop_front().unwrap(), | ||||||
|  | 			}).unwrap()); | ||||||
|  | 		map_stmts.push(quote_stmt!(cx, total = total + size;).unwrap()); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	let read_expr = match fields.iter().any(|f| codegen::has_ptr(&f.ty)) { | ||||||
|  | 		true => { | ||||||
|  | 			// cannot create structs with pointers
 | ||||||
|  | 			quote_expr!(cx, Err(::ipc::binary::BinaryConvertError)) | ||||||
|  | 		}, | ||||||
|  | 		false => { | ||||||
|  | 			if value_ident.is_some() { | ||||||
|  | 				let instance_create = named_fields_sequence(cx, &ty, fields); | ||||||
|  | 				quote_expr!(cx, { $map_stmts; $instance_create; Ok(result) }) | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				let map_variant = P(fields_sequence(cx, &ty, fields, &instance_ident.unwrap_or(builder.id("Self")))); | ||||||
|  | 				quote_expr!(cx, { $map_stmts; Ok($map_variant) }) | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  |     Ok(BinaryExpressions { | ||||||
|  | 		size: total_size_expr, | ||||||
|  | 		write: quote_expr!(cx, { $write_stmts; Ok(()) } ), | ||||||
|  | 		read: read_expr, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[allow(unreachable_code)] | ||||||
|  | fn binary_expr_item_struct( | ||||||
|  | 	cx: &ExtCtxt, | ||||||
|  | 	builder: &aster::AstBuilder, | ||||||
|  | 	_impl_generics: &ast::Generics, | ||||||
|  | 	ty: P<ast::Ty>, | ||||||
|  | 	span: Span, | ||||||
|  | 	variant_data: &ast::VariantData, | ||||||
|  | ) -> Result<BinaryExpressions, Error> { | ||||||
|  | 	match *variant_data { | ||||||
|  | 		ast::VariantData::Tuple(ref fields, _) => { | ||||||
|  | 			binary_expr_struct( | ||||||
|  | 				cx, | ||||||
|  | 				&builder, | ||||||
|  | 				ty, | ||||||
|  | 				fields, | ||||||
|  | 				Some(builder.id("self")), | ||||||
|  | 				None, | ||||||
|  | 			) | ||||||
|  | 		}, | ||||||
|  | 		ast::VariantData::Struct(ref fields, _) => { | ||||||
|  | 			binary_expr_struct( | ||||||
|  | 				cx, | ||||||
|  | 				&builder, | ||||||
|  | 				ty, | ||||||
|  | 				fields, | ||||||
|  | 				Some(builder.id("self")), | ||||||
|  | 				None, | ||||||
|  | 			) | ||||||
|  | 		}, | ||||||
|  | 		_ => { | ||||||
|  | 			cx.span_bug(span, | ||||||
|  | 				&format!("#[derive(Binary)] Unsupported struct content, expected tuple/struct, found: {:?}", | ||||||
|  | 					variant_data)); | ||||||
|  | 			Err(Error) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn binary_expr_enum( | ||||||
|  | 	cx: &ExtCtxt, | ||||||
|  | 	builder: &aster::AstBuilder, | ||||||
|  | 	type_ident: Ident, | ||||||
|  | 	impl_generics: &ast::Generics, | ||||||
|  | 	ty: P<ast::Ty>, | ||||||
|  | 	span: Span, | ||||||
|  | 	enum_def: &ast::EnumDef, | ||||||
|  | ) -> Result<BinaryExpressions, Error> { | ||||||
|  | 	let arms: Vec<_> = try!( | ||||||
|  | 		enum_def.variants.iter() | ||||||
|  | 			.enumerate() | ||||||
|  | 			.map(|(variant_index, variant)| { | ||||||
|  | 				binary_expr_variant( | ||||||
|  | 					cx, | ||||||
|  | 					builder, | ||||||
|  | 					type_ident, | ||||||
|  | 					impl_generics, | ||||||
|  | 					ty.clone(), | ||||||
|  | 					span, | ||||||
|  | 					variant, | ||||||
|  | 					variant_index, | ||||||
|  | 				) | ||||||
|  | 			}) | ||||||
|  | 			.collect() | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	let (size_arms, write_arms, mut read_arms) = ( | ||||||
|  | 		arms.iter().map(|x| x.size.clone()).collect::<Vec<ast::Arm>>(), | ||||||
|  | 		arms.iter().map(|x| x.write.clone()).collect::<Vec<ast::Arm>>(), | ||||||
|  | 		arms.iter().map(|x| x.read.clone()).collect::<Vec<ast::Arm>>()); | ||||||
|  | 
 | ||||||
|  | 	read_arms.push(quote_arm!(cx, _ => { Err(BinaryConvertError) } )); | ||||||
|  | 
 | ||||||
|  | 	Ok(BinaryExpressions { | ||||||
|  | 		size: quote_expr!(cx, 1usize + match *self { $size_arms }), | ||||||
|  | 		write: quote_expr!(cx, match *self { $write_arms }; ), | ||||||
|  | 		read: quote_expr!(cx, match buffer[0] { $read_arms }), | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct BinaryArm { | ||||||
|  | 	size: ast::Arm, | ||||||
|  | 	write: ast::Arm, | ||||||
|  | 	read: ast::Arm, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn fields_sequence( | ||||||
|  | 	ext_cx: &ExtCtxt, | ||||||
|  | 	_ty: &P<ast::Ty>, | ||||||
|  | 	fields: &[ast::StructField], | ||||||
|  | 	variant_ident: &ast::Ident, | ||||||
|  | ) -> ast::Expr { | ||||||
|  | 	use syntax::parse::token; | ||||||
|  | 	use syntax::ast::TokenTree::Token; | ||||||
|  | 
 | ||||||
|  | 	::quasi::parse_expr_panic(&mut ::syntax::parse::new_parser_from_tts( | ||||||
|  | 		ext_cx.parse_sess(), | ||||||
|  | 		ext_cx.cfg(), | ||||||
|  | 		{ | ||||||
|  | 			let _sp = ext_cx.call_site(); | ||||||
|  | 			let mut tt = ::std::vec::Vec::new(); | ||||||
|  | 			tt.push(Token(_sp, token::Ident(variant_ident.clone(), token::Plain))); | ||||||
|  | 			tt.push(Token(_sp, token::OpenDelim(token::Paren))); | ||||||
|  | 
 | ||||||
|  | 			for (idx, field) in fields.iter().enumerate() { | ||||||
|  | 				if field.ident.is_some() { | ||||||
|  | 					tt.push(Token(_sp, token::Ident(field.ident.clone().unwrap(), token::Plain))); | ||||||
|  | 					tt.push(Token(_sp, token::Colon)); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!"), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Paren))); | ||||||
|  | 				tt.push(Token( | ||||||
|  | 					_sp, | ||||||
|  | 					token::Ident( | ||||||
|  | 						ext_cx.ident_of(&::syntax::print::pprust::ty_to_string(&field.ty)), | ||||||
|  | 						token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::ModSep)); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("from_bytes"), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Paren))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::BinOp(token::And))); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"), token::Plain))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Bracket))); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Bracket))); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Bracket))); | ||||||
|  | 				tt.push(Token(_sp, token::DotDot)); | ||||||
|  | 
 | ||||||
|  | 				if idx+1 != fields.len() { | ||||||
|  | 					tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"), token::Plain))); | ||||||
|  | 					tt.push(Token(_sp, token::OpenDelim(token::Bracket))); | ||||||
|  | 					tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)), token::Plain))); | ||||||
|  | 					tt.push(Token(_sp, token::CloseDelim(token::Bracket))); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Bracket))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::Comma)); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("length_stack"), token::Plain))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Paren))); | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Paren))); | ||||||
|  | 				tt.push(Token(_sp, token::Comma)); | ||||||
|  | 			} | ||||||
|  | 			tt.push(Token(_sp, token::CloseDelim(token::Paren))); | ||||||
|  | 
 | ||||||
|  | 			tt | ||||||
|  | 		}) | ||||||
|  | 	).unwrap() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn named_fields_sequence( | ||||||
|  | 	ext_cx: &ExtCtxt, | ||||||
|  | 	ty: &P<ast::Ty>, | ||||||
|  | 	fields: &[ast::StructField], | ||||||
|  | ) -> ast::Stmt { | ||||||
|  | 	use syntax::parse::token; | ||||||
|  | 	use syntax::ast::TokenTree::Token; | ||||||
|  | 
 | ||||||
|  | 	::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts( | ||||||
|  | 		ext_cx.parse_sess(), | ||||||
|  | 		ext_cx.cfg(), | ||||||
|  | 		{ | ||||||
|  | 			let _sp = ext_cx.call_site(); | ||||||
|  | 			let mut tt = ::std::vec::Vec::new(); | ||||||
|  | 			tt.push(Token(_sp, token::Ident(ext_cx.ident_of("let"), token::Plain))); | ||||||
|  | 			tt.push(Token(_sp, token::Ident(ext_cx.ident_of("result"), token::Plain))); | ||||||
|  | 			tt.push(Token(_sp, token::Eq)); | ||||||
|  | 
 | ||||||
|  | 			tt.push(Token( | ||||||
|  | 				_sp, | ||||||
|  | 			  	token::Ident( | ||||||
|  | 					ext_cx.ident_of(&::syntax::print::pprust::ty_to_string(ty)), | ||||||
|  | 			 		token::Plain))); | ||||||
|  | 
 | ||||||
|  | 			tt.push(Token(_sp, token::OpenDelim(token::Brace))); | ||||||
|  | 
 | ||||||
|  | 			for (idx, field) in fields.iter().enumerate() { | ||||||
|  | 				tt.push(Token(_sp, token::Ident(field.ident.clone().unwrap(), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::Colon)); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!"), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Paren))); | ||||||
|  | 				tt.push(Token( | ||||||
|  | 					_sp, | ||||||
|  | 					token::Ident( | ||||||
|  | 						ext_cx.ident_of(&::syntax::print::pprust::ty_to_string(&field.ty)), | ||||||
|  | 						token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::ModSep)); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("from_bytes"), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Paren))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::BinOp(token::And))); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer"), token::Plain))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Bracket))); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::OpenDelim(token::Bracket))); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx)), token::Plain))); | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Bracket))); | ||||||
|  | 				tt.push(Token(_sp, token::DotDot)); | ||||||
|  | 				if idx + 1 != fields.len() { | ||||||
|  | 					tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map"), token::Plain))); | ||||||
|  | 					tt.push(Token(_sp, token::OpenDelim(token::Bracket))); | ||||||
|  | 					tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx+1)), token::Plain))); | ||||||
|  | 					tt.push(Token(_sp, token::CloseDelim(token::Bracket))); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Bracket))); | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::Comma)); | ||||||
|  | 				tt.push(Token(_sp, token::Ident(ext_cx.ident_of("length_stack"), token::Plain))); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Paren))); | ||||||
|  | 				tt.push(Token(_sp, token::CloseDelim(token::Paren))); | ||||||
|  | 				tt.push(Token(_sp, token::Comma)); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			tt.push(Token(_sp, token::CloseDelim(token::Brace))); | ||||||
|  | 			tt | ||||||
|  | 		}) | ||||||
|  | 	).unwrap() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn binary_expr_variant( | ||||||
|  | 	cx: &ExtCtxt, | ||||||
|  | 	builder: &aster::AstBuilder, | ||||||
|  | 	type_ident: Ident, | ||||||
|  | 	_generics: &ast::Generics, | ||||||
|  | 	ty: P<ast::Ty>, | ||||||
|  | 	_span: Span, | ||||||
|  | 	variant: &ast::Variant, | ||||||
|  | 	variant_index: usize, | ||||||
|  | ) -> Result<BinaryArm, Error> { | ||||||
|  | 	let variant_ident = variant.node.name; | ||||||
|  | 	let variant_index_ident = builder.id(format!("{}", variant_index)); | ||||||
|  | 
 | ||||||
|  | 	match variant.node.data { | ||||||
|  | 		ast::VariantData::Unit(_) => { | ||||||
|  | 			let pat = builder.pat().path() | ||||||
|  | 				.id(type_ident).id(variant_ident) | ||||||
|  | 				.build(); | ||||||
|  | 
 | ||||||
|  | 			let variant_val = builder.id(format!("{}::{}", type_ident, variant_ident)); | ||||||
|  | 
 | ||||||
|  | 			Ok(BinaryArm { | ||||||
|  | 				size: quote_arm!(cx, $pat => { 0usize } ), | ||||||
|  | 				write: quote_arm!(cx, $pat => { buffer[0] = $variant_index_ident; Ok(()) } ), | ||||||
|  | 				read: quote_arm!(cx, $variant_index_ident => { Ok($variant_val) } ), | ||||||
|  | 			}) | ||||||
|  | 		}, | ||||||
|  | 		ast::VariantData::Tuple(ref fields, _) => { | ||||||
|  | 			let field_names: Vec<ast::Ident> = (0 .. fields.len()) | ||||||
|  | 				.map(|i| builder.id(format!("__field{}", i))) | ||||||
|  | 				.collect(); | ||||||
|  | 
 | ||||||
|  | 			let pat = builder.pat().enum_() | ||||||
|  | 				.id(type_ident).id(variant_ident).build() | ||||||
|  | 				.with_pats( | ||||||
|  | 					field_names.iter() | ||||||
|  | 						.map(|field| builder.pat().ref_id(field)) | ||||||
|  | 				) | ||||||
|  | 				.build(); | ||||||
|  | 
 | ||||||
|  | 			let binary_expr = try!(binary_expr_struct( | ||||||
|  | 				cx, | ||||||
|  | 				&builder, | ||||||
|  | 				ty, | ||||||
|  | 				fields, | ||||||
|  | 				None, | ||||||
|  | 				Some(builder.id(format!("{}::{}", type_ident, variant_ident))), | ||||||
|  | 			)); | ||||||
|  | 
 | ||||||
|  | 			let (size_expr, write_expr, read_expr) = (binary_expr.size, vec![binary_expr.write], binary_expr.read); | ||||||
|  | 			Ok(BinaryArm { | ||||||
|  | 				size: quote_arm!(cx, $pat => { $size_expr } ), | ||||||
|  | 				write: quote_arm!(cx, | ||||||
|  | 					$pat => { | ||||||
|  | 						buffer[0] = $variant_index_ident; | ||||||
|  | 						let buffer = &mut buffer[1..]; | ||||||
|  | 						$write_expr | ||||||
|  | 				}), | ||||||
|  | 				read: quote_arm!(cx, $variant_index_ident => { $read_expr } ), | ||||||
|  | 			}) | ||||||
|  | 		}, | ||||||
|  | 		ast::VariantData::Struct(ref fields, _) => { | ||||||
|  | 			let field_names: Vec<_> = (0 .. fields.len()) | ||||||
|  | 				.map(|i| builder.id(format!("__field{}", i))) | ||||||
|  | 				.collect(); | ||||||
|  | 
 | ||||||
|  | 			let pat = builder.pat().struct_() | ||||||
|  | 				.id(type_ident).id(variant_ident).build() | ||||||
|  | 				.with_pats( | ||||||
|  | 					field_names.iter() | ||||||
|  | 						.zip(fields.iter()) | ||||||
|  | 						.map(|(id, field)|(field.ident.unwrap(), builder.pat().ref_id(id)))) | ||||||
|  | 				.build(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 			let binary_expr = try!(binary_expr_struct( | ||||||
|  | 				cx, | ||||||
|  | 				&builder, | ||||||
|  | 				ty, | ||||||
|  | 				fields, | ||||||
|  | 				None, | ||||||
|  | 				Some(builder.id(format!("{}::{}", type_ident, variant_ident))), | ||||||
|  | 			)); | ||||||
|  | 
 | ||||||
|  | 			let (size_expr, write_expr, read_expr) = (binary_expr.size, vec![binary_expr.write], binary_expr.read); | ||||||
|  | 
 | ||||||
|  | 			Ok(BinaryArm { | ||||||
|  | 				size: quote_arm!(cx, $pat => { $size_expr } ), | ||||||
|  | 				write: quote_arm!(cx, | ||||||
|  | 					$pat => { | ||||||
|  | 						buffer[0] = $variant_index_ident; | ||||||
|  | 						let buffer = &mut buffer[1..]; | ||||||
|  | 						$write_expr | ||||||
|  | 				}), | ||||||
|  | 				read: quote_arm!(cx, $pat => { $read_expr } ), | ||||||
|  | 			}) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -50,7 +50,8 @@ fn is_new_entry(path: &Path) -> Option<String> { | |||||||
| 			ident == "H256"      || | 			ident == "H256"      || | ||||||
| 			ident == "U256"      || | 			ident == "U256"      || | ||||||
| 			ident == "H2048"     || | 			ident == "H2048"     || | ||||||
| 			ident == "Address" | 			ident == "Address"   || | ||||||
|  | 			ident == "Bytes" | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -10,3 +10,4 @@ license = "GPL-3.0" | |||||||
| ethcore-devtools = { path = "../../devtools" } | ethcore-devtools = { path = "../../devtools" } | ||||||
| semver = "0.2.0" | semver = "0.2.0" | ||||||
| nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } | nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } | ||||||
|  | ethcore-util = { path = "../../util" } | ||||||
|  | |||||||
							
								
								
									
										357
									
								
								ipc/rpc/src/binary.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								ipc/rpc/src/binary.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,357 @@ | |||||||
|  | // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity 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 General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | //! Binary representation of types
 | ||||||
|  | 
 | ||||||
|  | use util::bytes::Populatable; | ||||||
|  | use std::mem; | ||||||
|  | use std::collections::VecDeque; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct BinaryConvertError; | ||||||
|  | 
 | ||||||
|  | pub trait BinaryConvertable : Sized { | ||||||
|  | 	fn size(&self) -> usize { | ||||||
|  | 		mem::size_of::<Self>() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError>; | ||||||
|  | 
 | ||||||
|  | 	fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError>; | ||||||
|  | 
 | ||||||
|  | 	fn from_empty_bytes() -> Result<Self, BinaryConvertError> { | ||||||
|  | 		Err(BinaryConvertError) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn len_params() -> usize { | ||||||
|  | 		0 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> BinaryConvertable for Option<T> where T: BinaryConvertable { | ||||||
|  | 	fn size(&self) -> usize { | ||||||
|  | 		match * self { None => 0, Some(ref val) => val.size() } | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> { | ||||||
|  | 		match *self { None => Err(BinaryConvertError), Some(ref val) => val.to_bytes(buffer, length_stack) } | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> { | ||||||
|  | 		Ok(Some(try!(T::from_bytes(buffer, length_stack)))) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn from_empty_bytes() -> Result<Self, BinaryConvertError> { | ||||||
|  | 		Ok(None) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn len_params() -> usize { | ||||||
|  | 		1 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> BinaryConvertable for Vec<T> where T: BinaryConvertable { | ||||||
|  | 	fn size(&self) -> usize { | ||||||
|  | 		match T::len_params() { | ||||||
|  | 			0 => mem::size_of::<T>() * self.len(), | ||||||
|  | 			_ => self.iter().fold(0usize, |acc, t| acc + t.size()), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> { | ||||||
|  | 		let mut offset = 0usize; | ||||||
|  | 		for item in self.iter() { | ||||||
|  | 			let next_size = match T::len_params() { | ||||||
|  | 				0 => mem::size_of::<T>(), | ||||||
|  | 				_ => { let size = item.size(); length_stack.push_back(size); size }, | ||||||
|  | 			}; | ||||||
|  | 			if next_size > 0 { | ||||||
|  | 				let item_end = offset + next_size; | ||||||
|  | 				try!(item.to_bytes(&mut buffer[offset..item_end], length_stack)); | ||||||
|  | 				offset = item_end; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		Ok(()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> { | ||||||
|  | 		let mut index = 0; | ||||||
|  | 		let mut result = Self::with_capacity( | ||||||
|  | 			match T::len_params() { | ||||||
|  | 				0 => buffer.len() /  mem::size_of::<T>(), | ||||||
|  | 				_ => 128, | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 		loop { | ||||||
|  | 			let next_size = match T::len_params() { | ||||||
|  | 				0 => mem::size_of::<T>(), | ||||||
|  | 				_ => try!(length_stack.pop_front().ok_or(BinaryConvertError)), | ||||||
|  | 			}; | ||||||
|  | 			let item = if next_size == 0 { | ||||||
|  | 				try!(T::from_empty_bytes()) | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				try!(T::from_bytes(&buffer[index..index+next_size], length_stack)) | ||||||
|  | 			}; | ||||||
|  | 			result.push(item); | ||||||
|  | 
 | ||||||
|  | 			index = index + next_size; | ||||||
|  | 			if index == buffer.len() { break; } | ||||||
|  | 			if index > buffer.len() { | ||||||
|  | 				return Err(BinaryConvertError) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		Ok(result) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn from_empty_bytes() -> Result<Self, BinaryConvertError> { | ||||||
|  | 		Ok(Self::new()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn len_params() -> usize { | ||||||
|  | 		1 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn deserialize_from<T, R>(r: &mut R) -> Result<T, BinaryConvertError> | ||||||
|  | 	where R: ::std::io::Read, | ||||||
|  | 		T: BinaryConvertable | ||||||
|  | { | ||||||
|  | 	let mut fake_stack = VecDeque::new(); | ||||||
|  | 	let mut length_stack = VecDeque::<usize>::new(); | ||||||
|  | 	let mut size_buffer = [0u8; 8]; | ||||||
|  | 	try!(r.read(&mut size_buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 	let stack_len = try!(u64::from_bytes(&mut size_buffer[..], &mut fake_stack)) as usize; | ||||||
|  | 	if stack_len > 0 { | ||||||
|  | 		let mut header_buffer = Vec::with_capacity(stack_len * 8); | ||||||
|  | 		unsafe {  header_buffer.set_len(stack_len * 8); }; | ||||||
|  | 
 | ||||||
|  | 		try!(r.read(&mut header_buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 		for idx in 0..stack_len { | ||||||
|  | 			let stack_item = try!(u64::from_bytes(&header_buffer[idx*8..(idx+1)*8], &mut fake_stack)); | ||||||
|  | 			length_stack.push_back(stack_item as usize); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	try!(r.read(&mut size_buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 	let size = try!(u64::from_bytes(&size_buffer[..], &mut fake_stack)) as usize; | ||||||
|  | 
 | ||||||
|  | 	let mut data = Vec::with_capacity(size); | ||||||
|  | 	unsafe { data.set_len(size) }; | ||||||
|  | 	try!(r.read(&mut data).map_err(|_| BinaryConvertError)); | ||||||
|  | 
 | ||||||
|  | 	T::from_bytes(&data[..], &mut length_stack) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn serialize_into<T, W>(t: &T, w: &mut W) -> Result<(), BinaryConvertError> | ||||||
|  | 	where W: ::std::io::Write, | ||||||
|  | 		T: BinaryConvertable | ||||||
|  | { | ||||||
|  | 	let mut length_stack = VecDeque::<usize>::new(); | ||||||
|  | 	let mut fake_stack = VecDeque::new(); | ||||||
|  | 	let mut size_buffer = [0u8; 8]; | ||||||
|  | 
 | ||||||
|  | 	let size = t.size(); | ||||||
|  | 	let mut buffer = Vec::with_capacity(size); | ||||||
|  | 	unsafe { buffer.set_len(size); } | ||||||
|  | 	try!(t.to_bytes(&mut buffer[..], &mut length_stack)); | ||||||
|  | 
 | ||||||
|  | 	let stack_len = length_stack.len(); | ||||||
|  | 	try!((stack_len as u64).to_bytes(&mut size_buffer[..], &mut fake_stack)); | ||||||
|  | 	try!(w.write(&size_buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 	if stack_len > 0 { | ||||||
|  | 		let mut header_buffer = Vec::with_capacity(stack_len * 8); | ||||||
|  | 		unsafe {  header_buffer.set_len(stack_len * 8); }; | ||||||
|  | 		try!((stack_len as u64).to_bytes(&mut header_buffer[0..8], &mut fake_stack)); | ||||||
|  | 		let mut idx = 0; | ||||||
|  | 		loop { | ||||||
|  | 			match length_stack.pop_front() { | ||||||
|  | 				Some(val) => try!((val as u64).to_bytes(&mut header_buffer[idx * 8..(idx+1) * 8], &mut fake_stack)), | ||||||
|  | 				None => { break; } | ||||||
|  | 			} | ||||||
|  | 			idx = idx + 1; | ||||||
|  | 		} | ||||||
|  | 		try!(w.write(&header_buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	try!((size as u64).to_bytes(&mut size_buffer[..], &mut fake_stack)); | ||||||
|  | 	try!(w.write(&size_buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 
 | ||||||
|  | 	try!(w.write(&buffer[..]).map_err(|_| BinaryConvertError)); | ||||||
|  | 
 | ||||||
|  | 	Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! binary_fixed_size { | ||||||
|  | 	($target_ty: ident) => { | ||||||
|  | 		impl BinaryConvertable for $target_ty { | ||||||
|  | 			fn from_bytes(bytes: &[u8], _length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> { | ||||||
|  | 				match bytes.len().cmp(&::std::mem::size_of::<$target_ty>()) { | ||||||
|  | 					::std::cmp::Ordering::Less => return Err(BinaryConvertError), | ||||||
|  | 					::std::cmp::Ordering::Greater => return Err(BinaryConvertError), | ||||||
|  | 					::std::cmp::Ordering::Equal => () | ||||||
|  | 				}; | ||||||
|  | 				let mut res: Self = unsafe { ::std::mem::uninitialized() }; | ||||||
|  | 				res.copy_raw(bytes); | ||||||
|  | 				Ok(res) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			fn to_bytes(&self, buffer: &mut [u8], _length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> { | ||||||
|  | 				let sz = ::std::mem::size_of::<$target_ty>(); | ||||||
|  | 				let ip: *const $target_ty = self; | ||||||
|  | 				let ptr: *const u8 = ip as *const _; | ||||||
|  | 				unsafe { | ||||||
|  | 					::std::ptr::copy(ptr, buffer.as_mut_ptr(), sz); | ||||||
|  | 				} | ||||||
|  | 				Ok(()) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | binary_fixed_size!(u64); | ||||||
|  | binary_fixed_size!(u32); | ||||||
|  | binary_fixed_size!(bool); | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn vec_serialize() { | ||||||
|  | 	let mut v = Vec::new(); | ||||||
|  | 	v.push(5u64); | ||||||
|  | 	v.push(10u64); | ||||||
|  | 	let mut length_stack = VecDeque::new(); | ||||||
|  | 	let mut data = Vec::with_capacity(v.size()); | ||||||
|  | 	unsafe { data.set_len(v.size()); } | ||||||
|  | 	let result = v.to_bytes(&mut data[..], &mut length_stack); | ||||||
|  | 
 | ||||||
|  | 	assert!(result.is_ok()); | ||||||
|  | 	assert_eq!(5, data[0]); | ||||||
|  | 	assert_eq!(0, data[1]); | ||||||
|  | 	assert_eq!(10, data[8]); | ||||||
|  | 	assert_eq!(0, data[12]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn calculates_size() { | ||||||
|  | 	let mut v = Vec::new(); | ||||||
|  | 	v.push(5u64); | ||||||
|  | 	v.push(10u64); | ||||||
|  | 
 | ||||||
|  | 	assert_eq!(16, v.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn vec_deserialize() { | ||||||
|  | 	let data = [ | ||||||
|  | 		10u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, | ||||||
|  | 		5u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, | ||||||
|  | 	]; | ||||||
|  | 
 | ||||||
|  | 	let mut length_stack = VecDeque::new(); | ||||||
|  | 	let vec = Vec::<u64>::from_bytes(&data[..], &mut length_stack).unwrap(); | ||||||
|  | 
 | ||||||
|  | 	assert_eq!(vec![10u64, 5u64], vec); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn vec_deserialize_chained() { | ||||||
|  | 	let mut v = Vec::new(); | ||||||
|  | 	v.push(Some(5u64)); | ||||||
|  | 	v.push(Some(10u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(12u64)); | ||||||
|  | 
 | ||||||
|  | 	let mut length_stack = VecDeque::new(); | ||||||
|  | 	let mut data = Vec::with_capacity(v.size()); | ||||||
|  | 	unsafe { data.set_len(v.size()); } | ||||||
|  | 	let result = v.to_bytes(&mut data[..], &mut length_stack); | ||||||
|  | 
 | ||||||
|  | 	assert!(result.is_ok()); | ||||||
|  | 	assert_eq!(4, length_stack.len()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn vec_serialize_deserialize() { | ||||||
|  | 	let mut v = Vec::new(); | ||||||
|  | 	v.push(Some(5u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(10u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(12u64)); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	let mut data = Vec::with_capacity(v.size()); | ||||||
|  | 	unsafe { data.set_len(v.size()); } | ||||||
|  | 	let mut length_stack = VecDeque::new(); | ||||||
|  | 
 | ||||||
|  | 	v.to_bytes(&mut data[..], &mut length_stack).unwrap(); | ||||||
|  | 	let de_v = Vec::<Option<u64>>::from_bytes(&data[..], &mut length_stack).unwrap(); | ||||||
|  | 
 | ||||||
|  | 	assert_eq!(v, de_v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn serialize_into_ok() { | ||||||
|  | 	use std::io::Cursor; | ||||||
|  |     let mut buff = Cursor::new(vec![0; 128]); | ||||||
|  | 
 | ||||||
|  | 	let mut v = Vec::new(); | ||||||
|  | 	v.push(Some(5u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(10u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(12u64)); | ||||||
|  | 
 | ||||||
|  | 	serialize_into(&v, &mut buff).unwrap(); | ||||||
|  | 	assert_eq!(5, buff.get_ref()[0]); | ||||||
|  | 	assert_eq!(8, buff.get_ref()[8]); | ||||||
|  | 	assert_eq!(0, buff.get_ref()[16]); | ||||||
|  | 	assert_eq!(8, buff.get_ref()[24]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn deserialize_from_ok() { | ||||||
|  | 	use std::io::Cursor; | ||||||
|  |     let mut buff = Cursor::new(vec![ | ||||||
|  | 		0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, | ||||||
|  | 		16u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, | ||||||
|  | 		10u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, | ||||||
|  | 		5u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | 	let vec = deserialize_from::<Vec<u64>, _>(&mut buff).unwrap(); | ||||||
|  | 
 | ||||||
|  | 	assert_eq!(vec![10u64, 5u64], vec); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn serialize_into_deserialize_from() { | ||||||
|  | 	use std::io::{Cursor, SeekFrom, Seek}; | ||||||
|  | 
 | ||||||
|  | 	let mut buff = Cursor::new(vec![0u8; 1024]); | ||||||
|  | 	let mut v = Vec::new(); | ||||||
|  | 	v.push(Some(5u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(10u64)); | ||||||
|  | 	v.push(None); | ||||||
|  | 	v.push(Some(12u64)); | ||||||
|  | 
 | ||||||
|  | 	serialize_into(&v, &mut buff).unwrap(); | ||||||
|  | 	buff.seek(SeekFrom::Start(0)).unwrap(); | ||||||
|  | 	let de_v = deserialize_from::<Vec<Option<u64>>, _>(&mut buff).unwrap(); | ||||||
|  | 	assert_eq!(v, de_v); | ||||||
|  | } | ||||||
| @ -19,6 +19,9 @@ | |||||||
| extern crate ethcore_devtools as devtools; | extern crate ethcore_devtools as devtools; | ||||||
| extern crate semver; | extern crate semver; | ||||||
| extern crate nanomsg; | extern crate nanomsg; | ||||||
|  | extern crate ethcore_util as util; | ||||||
| 
 | 
 | ||||||
| pub mod interface; | pub mod interface; | ||||||
|  | pub mod binary; | ||||||
| pub use interface::{IpcInterface, IpcSocket, invoke, IpcConfig, Handshake, Error, WithSocket}; | pub use interface::{IpcInterface, IpcSocket, invoke, IpcConfig, Handshake, Error, WithSocket}; | ||||||
|  | pub use binary::{BinaryConvertable, BinaryConvertError}; | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								ipc/tests/binary.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								ipc/tests/binary.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity 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 General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | #![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
 | ||||||
|  | 
 | ||||||
|  | include!(concat!(env!("OUT_DIR"), "/binary.rs")); | ||||||
							
								
								
									
										38
									
								
								ipc/tests/binary.rs.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								ipc/tests/binary.rs.in
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | // Copyright 2015, 2016 Ethcore (UK) Ltd.
 | ||||||
|  | // This file is part of Parity.
 | ||||||
|  | 
 | ||||||
|  | // Parity is free software: you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License as published by
 | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||||||
|  | // (at your option) any later version.
 | ||||||
|  | 
 | ||||||
|  | // Parity 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 General Public License for more details.
 | ||||||
|  | 
 | ||||||
|  | // You should have received a copy of the GNU General Public License
 | ||||||
|  | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | use ipc::*; | ||||||
|  | use std::mem; | ||||||
|  | use std::collections::VecDeque; | ||||||
|  | 
 | ||||||
|  | #[derive(Binary)] | ||||||
|  | pub enum Root { | ||||||
|  | 	Top, | ||||||
|  | 	Middle(u32, u64), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Binary, PartialEq, Debug)] | ||||||
|  | pub struct DoubleRoot { | ||||||
|  | 	pub x1: u32, | ||||||
|  | 	pub x2: u64, | ||||||
|  | 	pub x3: u32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Binary, PartialEq, Debug)] | ||||||
|  | pub struct ReferenceStruct<'a> { | ||||||
|  | 	pub ref_data: &'a u64, | ||||||
|  | } | ||||||
| @ -60,4 +60,13 @@ pub fn main() { | |||||||
| 		registry.expand("", &src, &dst).unwrap(); | 		registry.expand("", &src, &dst).unwrap(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 	// ipc pass
 | ||||||
|  | 	{ | ||||||
|  | 		let src = Path::new("binary.rs.in"); | ||||||
|  | 		let dst = Path::new(&out_dir).join("binary.rs"); | ||||||
|  | 		let mut registry = syntex::Registry::new(); | ||||||
|  | 		codegen::register(&mut registry); | ||||||
|  | 		registry.expand("", &src, &dst).unwrap(); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ | |||||||
| mod tests { | mod tests { | ||||||
| 
 | 
 | ||||||
| 	use super::super::service::*; | 	use super::super::service::*; | ||||||
|  | 	use super::super::binary::*; | ||||||
| 	use super::super::nested::{DBClient,DBWriter}; | 	use super::super::nested::{DBClient,DBWriter}; | ||||||
| 	use ipc::*; | 	use ipc::*; | ||||||
| 	use devtools::*; | 	use devtools::*; | ||||||
| @ -143,4 +144,19 @@ mod tests { | |||||||
| 
 | 
 | ||||||
| 		assert!(result.is_ok()); | 		assert!(result.is_ok()); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	#[test] | ||||||
|  | 	fn can_serialize_dummy_structs() { | ||||||
|  | 		let mut socket = TestSocket::new(); | ||||||
|  | 
 | ||||||
|  | 		let struct_ = DoubleRoot { x1: 0, x2: 100, x3: 100000}; | ||||||
|  | 		let res = ::ipc::binary::serialize_into(&struct_, &mut socket); | ||||||
|  | 
 | ||||||
|  | 		assert!(res.is_ok()); | ||||||
|  | 
 | ||||||
|  | 		let mut read_socket = TestSocket::new_ready(socket.write_buffer.clone()); | ||||||
|  | 		let new_struct: DoubleRoot = ::ipc::binary::deserialize_from(&mut read_socket).unwrap(); | ||||||
|  | 
 | ||||||
|  | 		assert_eq!(struct_, new_struct); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,3 +29,4 @@ pub mod service; | |||||||
| mod examples; | mod examples; | ||||||
| mod over_nano; | mod over_nano; | ||||||
| mod nested; | mod nested; | ||||||
|  | mod binary; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user