Эх сурвалжийг харах

lang: new internal writer to use bpf mem syscalls (#1589)

Matthew Callens 3 жил өмнө
parent
commit
ad8aec247b

+ 1 - 0
CHANGELOG.md

@@ -15,6 +15,7 @@ incremented for features.
 
 * cli: Add `anchor clean` command that's the same as `cargo clean` but preserves keypairs inside `target/deploy` ([#1470](https://github.com/project-serum/anchor/issues/1470)).
 * lang: Add new `AccountSysvarMismatch` error code and test cases for sysvars ([#1535](https://github.com/project-serum/anchor/pull/1535)).
+* lang: Replace `std::io::Cursor` with a custom `Write` impl that uses the Solana mem syscalls ([#1589](https://github.com/project-serum/anchor/pull/1589)).
 * spl: Add support for revoke instruction ([#1493](https://github.com/project-serum/anchor/pull/1493)).
 * ts: Add provider parameter to `Spl.token` factory method ([#1597](https://github.com/project-serum/anchor/pull/1597)).
 

+ 3 - 2
lang/src/accounts/account.rs

@@ -1,5 +1,6 @@
 //! Account container that checks ownership on deserialization.
 
+use crate::bpf_writer::BpfWriter;
 use crate::error::ErrorCode;
 use crate::*;
 use solana_program::account_info::AccountInfo;
@@ -333,8 +334,8 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
             let info = self.to_account_info();
             let mut data = info.try_borrow_mut_data()?;
             let dst: &mut [u8] = &mut data;
-            let mut cursor = std::io::Cursor::new(dst);
-            self.account.try_serialize(&mut cursor)?;
+            let mut writer = BpfWriter::new(dst);
+            self.account.try_serialize(&mut writer)?;
         }
         Ok(())
     }

+ 3 - 2
lang/src/accounts/account_loader.rs

@@ -1,5 +1,6 @@
 //! Type facilitating on demand zero copy deserialization.
 
+use crate::bpf_writer::BpfWriter;
 use crate::error::ErrorCode;
 use crate::{
     Accounts, AccountsClose, AccountsExit, Owner, Result, ToAccountInfo, ToAccountInfos,
@@ -225,8 +226,8 @@ impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T>
     fn exit(&self, _program_id: &Pubkey) -> Result<()> {
         let mut data = self.acc_info.try_borrow_mut_data()?;
         let dst: &mut [u8] = &mut data;
-        let mut cursor = std::io::Cursor::new(dst);
-        cursor.write_all(&T::discriminator()).unwrap();
+        let mut writer = BpfWriter::new(dst);
+        writer.write_all(&T::discriminator()).unwrap();
         Ok(())
     }
 }

+ 3 - 2
lang/src/accounts/loader.rs

@@ -1,3 +1,4 @@
+use crate::bpf_writer::BpfWriter;
 use crate::error::ErrorCode;
 use crate::{
     Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas,
@@ -168,8 +169,8 @@ impl<'info, T: ZeroCopy> AccountsExit<'info> for Loader<'info, T> {
     fn exit(&self, _program_id: &Pubkey) -> Result<()> {
         let mut data = self.acc_info.try_borrow_mut_data()?;
         let dst: &mut [u8] = &mut data;
-        let mut cursor = std::io::Cursor::new(dst);
-        cursor.write_all(&T::discriminator()).unwrap();
+        let mut writer = BpfWriter::new(dst);
+        writer.write_all(&T::discriminator()).unwrap();
         Ok(())
     }
 }

+ 3 - 2
lang/src/accounts/program_account.rs

@@ -1,5 +1,6 @@
 #[allow(deprecated)]
 use crate::accounts::cpi_account::CpiAccount;
+use crate::bpf_writer::BpfWriter;
 use crate::error::ErrorCode;
 use crate::{
     AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Result,
@@ -98,8 +99,8 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info
         let info = self.to_account_info();
         let mut data = info.try_borrow_mut_data()?;
         let dst: &mut [u8] = &mut data;
-        let mut cursor = std::io::Cursor::new(dst);
-        self.inner.account.try_serialize(&mut cursor)?;
+        let mut writer = BpfWriter::new(dst);
+        self.inner.account.try_serialize(&mut writer)?;
         Ok(())
     }
 }

+ 3 - 2
lang/src/accounts/state.rs

@@ -1,5 +1,6 @@
 #[allow(deprecated)]
 use crate::accounts::cpi_account::CpiAccount;
+use crate::bpf_writer::BpfWriter;
 use crate::error::ErrorCode;
 use crate::{
     AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Result, ToAccountInfo,
@@ -148,8 +149,8 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info
         let info = self.to_account_info();
         let mut data = info.try_borrow_mut_data()?;
         let dst: &mut [u8] = &mut data;
-        let mut cursor = std::io::Cursor::new(dst);
-        self.inner.account.try_serialize(&mut cursor)?;
+        let mut writer = BpfWriter::new(dst);
+        self.inner.account.try_serialize(&mut writer)?;
         Ok(())
     }
 }

+ 46 - 0
lang/src/bpf_writer.rs

@@ -0,0 +1,46 @@
+use solana_program::program_memory::sol_memcpy;
+use std::cmp;
+use std::io::{self, Write};
+
+#[derive(Debug, Default)]
+pub struct BpfWriter<T> {
+    inner: T,
+    pos: u64,
+}
+
+impl<T> BpfWriter<T> {
+    pub fn new(inner: T) -> Self {
+        Self { inner, pos: 0 }
+    }
+}
+
+impl Write for BpfWriter<&mut [u8]> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        if self.pos >= self.inner.len() as u64 {
+            return Ok(0);
+        }
+
+        let amt = cmp::min(
+            self.inner.len().saturating_sub(self.pos as usize),
+            buf.len(),
+        );
+        sol_memcpy(&mut self.inner[(self.pos as usize)..], buf, amt);
+        self.pos += amt as u64;
+        Ok(amt)
+    }
+
+    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+        if self.write(buf)? == buf.len() {
+            Ok(())
+        } else {
+            Err(io::Error::new(
+                io::ErrorKind::WriteZero,
+                "failed to write whole buffer",
+            ))
+        }
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}

+ 3 - 2
lang/src/common.rs

@@ -1,3 +1,4 @@
+use crate::bpf_writer::BpfWriter;
 use crate::error::ErrorCode;
 use crate::prelude::error;
 use crate::Result;
@@ -14,8 +15,8 @@ pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info
     // Mark the account discriminator as closed.
     let mut data = info.try_borrow_mut_data()?;
     let dst: &mut [u8] = &mut data;
-    let mut cursor = std::io::Cursor::new(dst);
-    cursor
+    let mut writer = BpfWriter::new(dst);
+    writer
         .write_all(&crate::__private::CLOSED_ACCOUNT_DISCRIMINATOR)
         .map_err(|_| error!(ErrorCode::AccountDidNotSerialize))
 }

+ 1 - 0
lang/src/lib.rs

@@ -33,6 +33,7 @@ use std::io::Write;
 mod account_meta;
 pub mod accounts;
 mod bpf_upgradeable_state;
+mod bpf_writer;
 mod common;
 pub mod context;
 mod ctor;