use crate::backend::Backend;
use crate::error::{BarretenbergError, Result};
use std::io::{Read, Write};
use std::path::Path;
use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
pub struct PipeBackend {
stdin: ChildStdin,
stdout: ChildStdout,
process: Option<Child>,
}
impl PipeBackend {
pub fn new(bb_binary_path: impl AsRef<Path>, threads: Option<usize>) -> Result<Self> {
let mut cmd = Command::new(bb_binary_path.as_ref());
cmd.arg("msgpack")
.arg("run")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit());
if let Some(t) = threads {
cmd.env("HARDWARE_CONCURRENCY", t.to_string());
}
let mut process = cmd.spawn()
.map_err(|e| BarretenbergError::Backend(format!("Failed to spawn BB process: {}", e)))?;
let stdin = process.stdin.take()
.ok_or_else(|| BarretenbergError::Backend("Failed to get stdin handle".to_string()))?;
let stdout = process.stdout.take()
.ok_or_else(|| BarretenbergError::Backend("Failed to get stdout handle".to_string()))?;
if let Ok(Some(status)) = process.try_wait() {
return Err(BarretenbergError::Backend(
format!("BB process exited immediately with status: {}", status)
));
}
Ok(Self {
stdin,
stdout,
process: Some(process),
})
}
fn send_with_prefix(&mut self, data: &[u8]) -> Result<()> {
let len = data.len() as u32;
self.stdin.write_all(&len.to_le_bytes())
.map_err(|e| BarretenbergError::Ipc(format!("Failed to write length: {}", e)))?;
self.stdin.write_all(data)
.map_err(|e| BarretenbergError::Ipc(format!("Failed to write data: {}", e)))?;
self.stdin.flush()
.map_err(|e| BarretenbergError::Ipc(format!("Failed to flush stdin: {}", e)))?;
Ok(())
}
fn receive_with_prefix(&mut self) -> Result<Vec<u8>> {
let mut len_buf = [0u8; 4];
self.stdout.read_exact(&mut len_buf)
.map_err(|e| BarretenbergError::Ipc(format!("Failed to read length: {}", e)))?;
let len = u32::from_le_bytes(len_buf) as usize;
let mut data = vec![0u8; len];
self.stdout.read_exact(&mut data)
.map_err(|e| BarretenbergError::Ipc(format!("Failed to read data: {}", e)))?;
Ok(data)
}
}
impl Backend for PipeBackend {
fn call(&mut self, input: &[u8]) -> Result<Vec<u8>> {
self.send_with_prefix(input)?;
self.receive_with_prefix()
}
fn destroy(&mut self) -> Result<()> {
if let Some(mut process) = self.process.take() {
let _ = process.kill();
let _ = process.wait();
}
Ok(())
}
}
impl Drop for PipeBackend {
fn drop(&mut self) {
let _ = self.destroy();
}
}