// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #![recursion_limit = "256"] use std::vec::Vec; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Fields, Ident, Index, Member, Meta, NestedMeta, Type, }; /// The function that derives the recursive implementation for struct or enum. #[proc_macro_derive(MsgOnSocket, attributes(msg_on_socket))] pub fn msg_on_socket_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let impl_for_input = msg_socket_impl(input); impl_for_input.into() } fn msg_socket_impl(input: DeriveInput) -> TokenStream { if !input.generics.params.is_empty() { return quote! { compile_error!("derive(MsgSocket) does not support generic parameters"); }; } match input.data { Data::Struct(ds) => { if is_named_struct(&ds) { impl_for_named_struct(input.ident, ds) } else { impl_for_tuple_struct(input.ident, ds) } } Data::Enum(de) => impl_for_enum(input.ident, de), _ => quote! { compile_error!("derive(MsgSocket) only support struct and enum"); }, } } fn is_named_struct(ds: &DataStruct) -> bool { matches!(&ds.fields, Fields::Named(_)) } /************************** Named Struct Impls ********************************************/ struct StructField { member: Member, ty: Type, skipped: bool, } fn impl_for_named_struct(name: Ident, ds: DataStruct) -> TokenStream { let fields = get_struct_fields(ds); let uses_fd_impl = define_uses_fd_for_struct(&fields); let buffer_sizes_impls = define_buffer_size_for_struct(&fields); let read_buffer = define_read_buffer_for_struct(&name, &fields); let write_buffer = define_write_buffer_for_struct(&name, &fields); quote! { impl msg_socket::MsgOnSocket for #name { #uses_fd_impl #buffer_sizes_impls #read_buffer #write_buffer } } } // Flatten struct fields. fn get_struct_fields(ds: DataStruct) -> Vec { let fields = match ds.fields { Fields::Named(fields_named) => fields_named.named, _ => { panic!("Struct must have named fields"); } }; let mut vec = Vec::new(); for field in fields { let member = match field.ident { Some(ident) => Member::Named(ident), None => panic!("Unknown Error."), }; let ty = field.ty; let mut skipped = false; for attr in field .attrs .iter() .filter(|attr| attr.path.is_ident("msg_on_socket")) { match attr.parse_meta().unwrap() { Meta::List(meta) => { for nested in meta.nested { match nested { NestedMeta::Meta(Meta::Path(ref meta_path)) if meta_path.is_ident("skip") => { skipped = true; } _ => panic!("unrecognized attribute meta `{}`", quote! { #nested }), } } } _ => panic!("unrecognized attribute `{}`", quote! { #attr }), } } vec.push(StructField { member, ty, skipped, }); } vec } fn define_uses_fd_for_struct(fields: &[StructField]) -> TokenStream { let field_types: Vec<_> = fields .iter() .filter(|f| !f.skipped) .map(|f| &f.ty) .collect(); if field_types.is_empty() { return quote!(); } quote! { fn uses_fd() -> bool { #(<#field_types>::uses_fd())||* } } } fn define_buffer_size_for_struct(fields: &[StructField]) -> TokenStream { let (msg_size, fd_count) = get_fields_buffer_size_sum(fields); quote! { fn msg_size(&self) -> usize { #msg_size } fn fd_count(&self) -> usize { #fd_count } } } fn define_read_buffer_for_struct(_name: &Ident, fields: &[StructField]) -> TokenStream { let mut read_fields = Vec::new(); let mut init_fields = Vec::new(); for field in fields { let ident = match &field.member { Member::Named(ident) => ident, Member::Unnamed(_) => unreachable!(), }; let name = ident.clone(); if field.skipped { let ty = &field.ty; init_fields.push(quote! { #name: <#ty>::default() }); continue; } let read_field = read_from_buffer_and_move_offset(&ident, &field.ty); read_fields.push(read_field); init_fields.push(quote!(#name)); } quote! { unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let mut __offset = 0usize; let mut __fd_offset = 0usize; #(#read_fields)* Ok(( Self { #(#init_fields),* }, __fd_offset )) } } } fn define_write_buffer_for_struct(_name: &Ident, fields: &[StructField]) -> TokenStream { let mut write_fields = Vec::new(); for field in fields { if field.skipped { continue; } let ident = match &field.member { Member::Named(ident) => ident, Member::Unnamed(_) => unreachable!(), }; let write_field = write_to_buffer_and_move_offset(&ident); write_fields.push(write_field); } quote! { fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { let mut __offset = 0usize; let mut __fd_offset = 0usize; #(#write_fields)* Ok(__fd_offset) } } } /************************** Enum Impls ********************************************/ fn impl_for_enum(name: Ident, de: DataEnum) -> TokenStream { let uses_fd_impl = define_uses_fd_for_enum(&de); let buffer_sizes_impls = define_buffer_size_for_enum(&name, &de); let read_buffer = define_read_buffer_for_enum(&name, &de); let write_buffer = define_write_buffer_for_enum(&name, &de); quote! { impl msg_socket::MsgOnSocket for #name { #uses_fd_impl #buffer_sizes_impls #read_buffer #write_buffer } } } fn define_uses_fd_for_enum(de: &DataEnum) -> TokenStream { let mut variant_field_types = Vec::new(); for variant in &de.variants { for variant_field_ty in variant.fields.iter().map(|f| &f.ty) { variant_field_types.push(variant_field_ty); } } if variant_field_types.len() == 0 { return quote!(); } quote! { fn uses_fd() -> bool { #(<#variant_field_types>::uses_fd())||* } } } fn define_buffer_size_for_enum(name: &Ident, de: &DataEnum) -> TokenStream { let mut msg_size_match_variants = Vec::new(); let mut fd_count_match_variants = Vec::new(); for variant in &de.variants { let variant_name = &variant.ident; match &variant.fields { Fields::Named(fields) => { let mut tmp_names = Vec::new(); for field in &fields.named { tmp_names.push(field.ident.clone().unwrap()); } let v = quote! { #name::#variant_name { #(#tmp_names),* } => #(#tmp_names.msg_size())+*, }; msg_size_match_variants.push(v); let v = quote! { #name::#variant_name { #(#tmp_names),* } => #(#tmp_names.fd_count())+*, }; fd_count_match_variants.push(v); } Fields::Unnamed(fields) => { let mut tmp_names = Vec::new(); for idx in 0..fields.unnamed.len() { let tmp_name = format!("enum_field{}", idx); let tmp_name = Ident::new(&tmp_name, Span::call_site()); tmp_names.push(tmp_name.clone()); } let v = quote! { #name::#variant_name(#(#tmp_names),*) => #(#tmp_names.msg_size())+*, }; msg_size_match_variants.push(v); let v = quote! { #name::#variant_name(#(#tmp_names),*) => #(#tmp_names.fd_count())+*, }; fd_count_match_variants.push(v); } Fields::Unit => { let v = quote! { #name::#variant_name => 0, }; msg_size_match_variants.push(v.clone()); fd_count_match_variants.push(v); } } } quote! { fn msg_size(&self) -> usize { 1 + match self { #(#msg_size_match_variants)* } } fn fd_count(&self) -> usize { match self { #(#fd_count_match_variants)* } } } } fn define_read_buffer_for_enum(name: &Ident, de: &DataEnum) -> TokenStream { let mut match_variants = Vec::new(); let de = de.clone(); for (idx, variant) in de.variants.iter().enumerate() { let idx = idx as u8; let variant_name = &variant.ident; match &variant.fields { Fields::Named(fields) => { let mut tmp_names = Vec::new(); let mut read_tmps = Vec::new(); for f in &fields.named { tmp_names.push(f.ident.clone()); let read_tmp = read_from_buffer_and_move_offset(f.ident.as_ref().unwrap(), &f.ty); read_tmps.push(read_tmp); } let v = quote! { #idx => { let mut __offset = 1usize; let mut __fd_offset = 0usize; #(#read_tmps)* Ok((#name::#variant_name { #(#tmp_names),* }, __fd_offset)) } }; match_variants.push(v); } Fields::Unnamed(fields) => { let mut tmp_names = Vec::new(); let mut read_tmps = Vec::new(); for (idx, field) in fields.unnamed.iter().enumerate() { let tmp_name = format_ident!("enum_field{}", idx); tmp_names.push(tmp_name.clone()); let read_tmp = read_from_buffer_and_move_offset(&tmp_name, &field.ty); read_tmps.push(read_tmp); } let v = quote! { #idx => { let mut __offset = 1usize; let mut __fd_offset = 0usize; #(#read_tmps)* Ok((#name::#variant_name( #(#tmp_names),*), __fd_offset)) } }; match_variants.push(v); } Fields::Unit => { let v = quote! { #idx => Ok((#name::#variant_name, 0)), }; match_variants.push(v); } } } quote! { unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let v = buffer.get(0).ok_or(msg_socket::MsgError::WrongMsgBufferSize)?; match v { #(#match_variants)* _ => Err(msg_socket::MsgError::InvalidType), } } } } fn define_write_buffer_for_enum(name: &Ident, de: &DataEnum) -> TokenStream { let mut match_variants = Vec::new(); let de = de.clone(); for (idx, variant) in de.variants.iter().enumerate() { let idx = idx as u8; let variant_name = &variant.ident; match &variant.fields { Fields::Named(fields) => { let mut tmp_names = Vec::new(); let mut write_tmps = Vec::new(); for f in &fields.named { tmp_names.push(f.ident.clone().unwrap()); let write_tmp = enum_write_to_buffer_and_move_offset(&f.ident.as_ref().unwrap()); write_tmps.push(write_tmp); } let v = quote! { #name::#variant_name { #(#tmp_names),* } => { buffer[0] = #idx; let mut __offset = 1usize; let mut __fd_offset = 0usize; #(#write_tmps)* Ok(__fd_offset) } }; match_variants.push(v); } Fields::Unnamed(fields) => { let mut tmp_names = Vec::new(); let mut write_tmps = Vec::new(); for idx in 0..fields.unnamed.len() { let tmp_name = format_ident!("enum_field{}", idx); tmp_names.push(tmp_name.clone()); let write_tmp = enum_write_to_buffer_and_move_offset(&tmp_name); write_tmps.push(write_tmp); } let v = quote! { #name::#variant_name(#(#tmp_names),*) => { buffer[0] = #idx; let mut __offset = 1usize; let mut __fd_offset = 0usize; #(#write_tmps)* Ok(__fd_offset) } }; match_variants.push(v); } Fields::Unit => { let v = quote! { #name::#variant_name => { buffer[0] = #idx; Ok(0) } }; match_variants.push(v); } } } quote! { fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { if buffer.is_empty() { return Err(msg_socket::MsgError::WrongMsgBufferSize) } match self { #(#match_variants)* } } } } fn enum_write_to_buffer_and_move_offset(name: &Ident) -> TokenStream { quote! { let o = #name.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += #name.msg_size(); __fd_offset += o; } } /************************** Tuple Impls ********************************************/ fn impl_for_tuple_struct(name: Ident, ds: DataStruct) -> TokenStream { let fields = get_tuple_fields(ds); let uses_fd_impl = define_uses_fd_for_tuples(&fields); let buffer_sizes_impls = define_buffer_size_for_struct(&fields); let read_buffer = define_read_buffer_for_tuples(&name, &fields); let write_buffer = define_write_buffer_for_tuples(&name, &fields); quote! { impl msg_socket::MsgOnSocket for #name { #uses_fd_impl #buffer_sizes_impls #read_buffer #write_buffer } } } fn get_tuple_fields(ds: DataStruct) -> Vec { let mut field_idents = Vec::new(); let fields = match ds.fields { Fields::Unnamed(fields_unnamed) => fields_unnamed.unnamed, _ => { panic!("Tuple struct must have unnamed fields."); } }; for (idx, field) in fields.iter().enumerate() { let member = Member::Unnamed(Index::from(idx)); let ty = field.ty.clone(); field_idents.push(StructField { member, ty, skipped: false, }); } field_idents } fn define_uses_fd_for_tuples(fields: &[StructField]) -> TokenStream { if fields.len() == 0 { return quote!(); } let field_types = fields.iter().map(|f| &f.ty); quote! { fn uses_fd() -> bool { #(<#field_types>::uses_fd())||* } } } fn define_read_buffer_for_tuples(name: &Ident, fields: &[StructField]) -> TokenStream { let mut read_fields = Vec::new(); let mut init_fields = Vec::new(); for (idx, field) in fields.iter().enumerate() { let tmp_name = format!("tuple_tmp{}", idx); let tmp_name = Ident::new(&tmp_name, Span::call_site()); let read_field = read_from_buffer_and_move_offset(&tmp_name, &field.ty); read_fields.push(read_field); init_fields.push(quote!(#tmp_name)); } quote! { unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let mut __offset = 0usize; let mut __fd_offset = 0usize; #(#read_fields)* Ok(( #name ( #(#init_fields),* ), __fd_offset )) } } } fn define_write_buffer_for_tuples(name: &Ident, fields: &[StructField]) -> TokenStream { let mut write_fields = Vec::new(); let mut tmp_names = Vec::new(); for idx in 0..fields.len() { let tmp_name = format_ident!("tuple_tmp{}", idx); let write_field = enum_write_to_buffer_and_move_offset(&tmp_name); write_fields.push(write_field); tmp_names.push(tmp_name); } quote! { fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { let mut __offset = 0usize; let mut __fd_offset = 0usize; let #name( #(#tmp_names),* ) = self; #(#write_fields)* Ok(__fd_offset) } } } /************************** Helpers ********************************************/ fn get_fields_buffer_size_sum(fields: &[StructField]) -> (TokenStream, TokenStream) { let fields: Vec<_> = fields .iter() .filter(|f| !f.skipped) .map(|f| &f.member) .collect(); if fields.len() > 0 { ( quote! { #( self.#fields.msg_size() as usize )+* }, quote! { #( self.#fields.fd_count() as usize )+* }, ) } else { (quote!(0), quote!(0)) } } fn read_from_buffer_and_move_offset(name: &Ident, ty: &Type) -> TokenStream { quote! { let t = <#ty>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let #name = t.0; } } fn write_to_buffer_and_move_offset(name: &Ident) -> TokenStream { quote! { let o = self.#name.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += self.#name.msg_size(); __fd_offset += o; } } #[cfg(test)] mod tests { use super::define_uses_fd_for_enum; use crate::msg_socket_impl; use quote::quote; use syn::{parse_quote, Data, DeriveInput}; #[test] fn simple_enum_uses_fd() { let input: DeriveInput = parse_quote! { enum Simple { A, B, } }; let data = match input.data { Data::Enum(data) => data, _ => unreachable!(), }; assert_eq!(define_uses_fd_for_enum(&data).to_string(), "",); } #[test] fn end_to_end_struct_test() { let input: DeriveInput = parse_quote! { struct MyMsg { a: u8, b: RawFd, c: u32, } }; let expected = quote! { impl msg_socket::MsgOnSocket for MyMsg { fn uses_fd() -> bool { ::uses_fd() || ::uses_fd() || ::uses_fd() } fn msg_size(&self) -> usize { self.a.msg_size() as usize + self.b.msg_size() as usize + self.c.msg_size() as usize } fn fd_count(&self) -> usize { self.a.fd_count() as usize + self.b.fd_count() as usize + self.c.fd_count() as usize } unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let mut __offset = 0usize; let mut __fd_offset = 0usize; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let a = t.0; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let b = t.0; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let c = t.0; Ok((Self { a, b, c }, __fd_offset)) } fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { let mut __offset = 0usize; let mut __fd_offset = 0usize; let o = self .a .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += self.a.msg_size(); __fd_offset += o; let o = self .b .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += self.b.msg_size(); __fd_offset += o; let o = self .c .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += self.c.msg_size(); __fd_offset += o; Ok(__fd_offset) } } }; assert_eq!(msg_socket_impl(input).to_string(), expected.to_string()); } #[test] fn end_to_end_tuple_struct_test() { let input: DeriveInput = parse_quote! { struct MyMsg(u8, u32, File); }; let expected = quote! { impl msg_socket::MsgOnSocket for MyMsg { fn uses_fd() -> bool { ::uses_fd() || ::uses_fd() || ::uses_fd() } fn msg_size(&self) -> usize { self.0.msg_size() as usize + self.1.msg_size() as usize + self.2.msg_size() as usize } fn fd_count(&self) -> usize { self.0.fd_count() as usize + self.1.fd_count() as usize + self.2.fd_count() as usize } unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let mut __offset = 0usize; let mut __fd_offset = 0usize; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let tuple_tmp0 = t.0; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let tuple_tmp1 = t.0; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let tuple_tmp2 = t.0; Ok((MyMsg(tuple_tmp0, tuple_tmp1, tuple_tmp2), __fd_offset)) } fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { let mut __offset = 0usize; let mut __fd_offset = 0usize; let MyMsg(tuple_tmp0, tuple_tmp1, tuple_tmp2) = self; let o = tuple_tmp0.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += tuple_tmp0.msg_size(); __fd_offset += o; let o = tuple_tmp1.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += tuple_tmp1.msg_size(); __fd_offset += o; let o = tuple_tmp2.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += tuple_tmp2.msg_size(); __fd_offset += o; Ok(__fd_offset) } } }; assert_eq!(msg_socket_impl(input).to_string(), expected.to_string()); } #[test] fn end_to_end_enum_test() { let input: DeriveInput = parse_quote! { enum MyMsg { A(u8), B, C { f0: u8, f1: RawFd, }, } }; let expected = quote! { impl msg_socket::MsgOnSocket for MyMsg { fn uses_fd() -> bool { ::uses_fd() || ::uses_fd() || ::uses_fd() } fn msg_size(&self) -> usize { 1 + match self { MyMsg::A(enum_field0) => enum_field0.msg_size(), MyMsg::B => 0, MyMsg::C { f0, f1 } => f0.msg_size() + f1.msg_size(), } } fn fd_count(&self) -> usize { match self { MyMsg::A(enum_field0) => enum_field0.fd_count(), MyMsg::B => 0, MyMsg::C { f0, f1 } => f0.fd_count() + f1.fd_count(), } } unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let v = buffer .get(0) .ok_or(msg_socket::MsgError::WrongMsgBufferSize)?; match v { 0u8 => { let mut __offset = 1usize; let mut __fd_offset = 0usize; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let enum_field0 = t.0; Ok((MyMsg::A(enum_field0), __fd_offset)) } 1u8 => Ok((MyMsg::B, 0)), 2u8 => { let mut __offset = 1usize; let mut __fd_offset = 0usize; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let f0 = t.0; let t = ::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?; __offset += t.0.msg_size(); __fd_offset += t.1; let f1 = t.0; Ok((MyMsg::C { f0, f1 }, __fd_offset)) } _ => Err(msg_socket::MsgError::InvalidType), } } fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { if buffer.is_empty() { return Err(msg_socket::MsgError::WrongMsgBufferSize) } match self { MyMsg::A(enum_field0) => { buffer[0] = 0u8; let mut __offset = 1usize; let mut __fd_offset = 0usize; let o = enum_field0 .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += enum_field0.msg_size(); __fd_offset += o; Ok(__fd_offset) } MyMsg::B => { buffer[0] = 1u8; Ok(0) } MyMsg::C { f0, f1 } => { buffer[0] = 2u8; let mut __offset = 1usize; let mut __fd_offset = 0usize; let o = f0.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += f0.msg_size(); __fd_offset += o; let o = f1.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?; __offset += f1.msg_size(); __fd_offset += o; Ok(__fd_offset) } } } } }; assert_eq!(msg_socket_impl(input).to_string(), expected.to_string()); } #[test] fn end_to_end_struct_skip_test() { let input: DeriveInput = parse_quote! { struct MyMsg { #[msg_on_socket(skip)] a: u8, } }; let expected = quote! { impl msg_socket::MsgOnSocket for MyMsg { fn msg_size(&self) -> usize { 0 } fn fd_count(&self) -> usize { 0 } unsafe fn read_from_buffer( buffer: &[u8], fds: &[std::os::unix::io::RawFd], ) -> msg_socket::MsgResult<(Self, usize)> { let mut __offset = 0usize; let mut __fd_offset = 0usize; Ok((Self { a: ::default() }, __fd_offset)) } fn write_to_buffer( &self, buffer: &mut [u8], fds: &mut [std::os::unix::io::RawFd], ) -> msg_socket::MsgResult { let mut __offset = 0usize; let mut __fd_offset = 0usize; Ok(__fd_offset) } } }; assert_eq!(msg_socket_impl(input).to_string(), expected.to_string()); } }