[fidl][rust] Add fidl_struct_copy! macro

This add defines an Encodable and Decodable implementation that directly
copies the struct using zerocopy.

This will be generated in fidlgen_rust in a later CL. It can't be
generated in this CL because it will break rust code appearing in the
SDK.

Test: fx test fidl_rust_tests
Change-Id: I71502652ac0900dfa692d3f27d50e16797cc7fb7
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/403334
Commit-Queue: Benjamin Prosnitz <bprosnitz@google.com>
Reviewed-by: Mitchell Kember <mkember@google.com>
Testability-Review: Mitchell Kember <mkember@google.com>
diff --git a/src/lib/fidl/rust/fidl/src/encoding.rs b/src/lib/fidl/rust/fidl/src/encoding.rs
index 1688afe..a5dc8cf 100644
--- a/src/lib/fidl/rust/fidl/src/encoding.rs
+++ b/src/lib/fidl/rust/fidl/src/encoding.rs
@@ -1934,6 +1934,69 @@
     }
 }
 
+/// A macro which implements the FIDL `Encodable` and `Decodable` traits
+/// for an existing struct by doing a direct copy using zerocopy::AsBytes.
+#[macro_export]
+macro_rules! fidl_struct_copy {
+    (
+        name: $name:ty,
+        members: [$(
+            $member_name:ident {
+                ty: $member_ty:ty,
+                offset_v1: $member_offset_v1:expr,
+            },
+        )*],
+        size_v1: $size_v1:expr,
+        align_v1: $align_v1:expr,
+    ) => {
+        impl $crate::encoding::Layout for $name {
+            fn inline_align(_context: &$crate::encoding::Context) -> usize {
+                $align_v1
+            }
+
+            fn inline_size(_context: &$crate::encoding::Context) -> usize {
+                $size_v1
+            }
+            
+            fn slice_as_bytes(slice: &mut [Self]) -> Option<&mut [u8]> {
+                Some(zerocopy::AsBytes::as_bytes_mut(slice))
+            }
+        }
+
+        impl $crate::encoding::Encodable for $name {
+            fn encode(&mut self, encoder: &mut $crate::encoding::Encoder<'_>, offset: usize, _recursion_depth: usize) -> $crate::Result<()> {
+                let bytes = zerocopy::AsBytes::as_bytes(self);
+                encoder.buf[offset..offset + bytes.len()].copy_from_slice(bytes);
+                Ok(())
+            }
+        }
+
+        impl $crate::encoding::Decodable for $name {
+            fn new_empty() -> Self {
+                Self {
+                    $(
+                        $member_name: $crate::fidl_new_empty!($member_ty),
+                    )*
+                }
+            }
+
+            fn decode(&mut self, decoder: &mut $crate::encoding::Decoder<'_>) -> $crate::Result<()> {
+                let bytes: &mut [u8] = zerocopy::AsBytes::as_bytes_mut(self);
+                let size = bytes.len();
+                let offset = decoder.next_offset(size);
+                bytes.copy_from_slice(&decoder.buf[offset..offset + size]);
+                Ok(())
+            }
+        }
+
+        impl $crate::encoding::Autonull for $name {
+            fn naturally_nullable(_context: &$crate::encoding::Context) -> bool {
+                false
+            }
+        }
+    }
+}
+
 /// A macro which creates an empty struct and implements the FIDL `Encodable` and `Decodable`
 /// traits for it.
 #[macro_export]
@@ -3800,6 +3863,67 @@
         }
     }
 
+    #[derive(Debug, PartialEq, zerocopy::AsBytes, zerocopy::FromBytes)]
+    #[repr(C)]
+    pub struct DirectCopyStruct {
+        a: u64,
+        b: u32,
+        c: u16,
+        d: u16,
+    }
+    fidl_struct_copy! {
+        name: DirectCopyStruct,
+        members: [
+            a {
+                ty: u64,
+                offset_v1: 0,
+            },
+            b {
+                ty: u32,
+                offset_v1: 8,
+            },
+            c {
+                ty: u16,
+                offset_v1: 12,
+            },
+            d {
+                ty: u16,
+                offset_v1: 14,
+            },
+        ],
+        size_v1: 16,
+        align_v1: 8,
+    }
+
+    #[test]
+    fn direct_copy_struct_encode() {
+        let bytes = &[
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //
+            0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, //
+        ];
+        let mut obj =
+            DirectCopyStruct { a: 0x0807060504030201, b: 0x0c0b0a09, c: 0x0e0d, d: 0x100f };
+
+        for ctx in CONTEXTS {
+            encode_assert_bytes(ctx, &mut obj, bytes);
+        }
+    }
+
+    #[test]
+    fn direct_copy_struct_decode() {
+        let bytes = &[
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //
+            0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, //
+        ];
+        let obj = DirectCopyStruct { a: 0x0807060504030201, b: 0x0c0b0a09, c: 0x0e0d, d: 0x100f };
+
+        for ctx in CONTEXTS {
+            let mut out = DirectCopyStruct::new_empty();
+            Decoder::decode_with_context(ctx, bytes, &mut [], &mut out).expect("Decoding failed");
+            assert_eq!(out, obj);
+        }
+    }
+
     #[derive(Debug, PartialEq)]
     pub struct Int64Struct {
         x: u64,