[opentitanlib] Alternate to unsafe code for alignment

Signed-off-by: Jon Flatley <jflat@google.com>
diff --git a/sw/host/opentitanlib/src/image/image.rs b/sw/host/opentitanlib/src/image/image.rs
index 5353f83..72c8e4c 100644
--- a/sw/host/opentitanlib/src/image/image.rs
+++ b/sw/host/opentitanlib/src/image/image.rs
@@ -25,8 +25,24 @@
 
 #[repr(C)]
 #[derive(Debug)]
+pub struct ImageData {
+    pub bytes: [u8; Image::MAX_SIZE],
+    _align: [Manifest; 0],
+}
+
+impl Default for ImageData {
+    fn default() -> Self {
+        ImageData {
+            bytes: [0xFF; Image::MAX_SIZE],
+            _align: [],
+        }
+    }
+}
+
+#[derive(Debug, Default)]
 pub struct Image {
-    bytes: Vec<u8>,
+    data: Box<ImageData>,
+    size: usize,
 }
 
 #[derive(Debug)]
@@ -43,45 +59,33 @@
 }
 
 impl Image {
+    const MAX_SIZE: usize = 512 * 1024;
     /// Creates an `Image` from a given input binary.
+    pub fn from_reader(mut r: impl Read) -> Result<Self> {
+        let mut image = Image::default();
+        image.size = r.read(&mut image.data.bytes)?;
+        Ok(image)
+    }
+
+    pub fn to_writer(&self, w: &mut impl Write) -> Result<()> {
+        w.write_all(&self.data.bytes[..self.size])?;
+        Ok(())
+    }
+
     pub fn read_from_file(path: &Path) -> Result<Image> {
-        let file_len = path.metadata()?.len() as usize;
-        // Create a buffer with the same alignment as Manifest that's as least as long as the input
-        // file.
-        let mut aligned: Vec<Manifest> =
-            Vec::with_capacity((0..file_len).step_by(size_of::<Manifest>()).len());
-
-        // Convert the aligned buffer to a Vec<u8> with the same capacity, but length equal to the
-        // size of the input file.
-        let vec_ptr = aligned.as_mut_ptr() as *mut u8;
-        let vec_cap = aligned.capacity() * size_of::<Manifest>();
-
-        // Forget `aligned` so we don't double free.
-        std::mem::forget(aligned);
-
-        // Convert our `aligned` Vec<Manifest> to a Vec<u8> with the same capacity and len equal to
-        // the size of the input image. This should mean that the new Vec<u8> has the same
-        // alignment as Manifest so we can successfully use LayoutVerified later to reinterpret the
-        // head of the image as a Manifest.
-        let mut buf: Vec<u8> = unsafe { Vec::from_raw_parts(vec_ptr, file_len, vec_cap) };
-
-        // Read the image into our buffer.
-        let mut file = File::open(path)?;
-        file.read(&mut *buf)?;
-
-        Ok(Image { bytes: buf })
+        let file = File::open(path)?;
+        Self::from_reader(file)
     }
 
     /// Write out the `Image` to a file at the given `path`.
     pub fn write_to_file(self, path: &Path) -> Result<()> {
         let mut file = File::create(path)?;
-        file.write(self.bytes.as_slice())?;
-        Ok(())
+        self.to_writer(&mut file)
     }
 
     /// Overwrites all fields in the image's manifest that are defined in `other`.
     pub fn overwrite_manifest(&mut self, other: ManifestDef) -> Result<()> {
-        let (manifest_slice, _) = self.bytes.split_at_mut(size_of::<Manifest>());
+        let manifest_slice = &mut self.data.bytes[0..size_of::<Manifest>()];
         let manifest_layout: LayoutVerified<&mut [u8], Manifest> =
             LayoutVerified::new(&mut *manifest_slice).ok_or(ImageError::Parse)?;
         let manifest: &mut Manifest = manifest_layout.into_mut();