use super::validation::DependencyValidator;
use crate::error::{Result, SaferRingError};
use crate::operation::{Building, Operation};
use std::collections::HashMap;
#[derive(Debug)]
pub struct Batch<'ring, 'buf> {
operations: Vec<BatchOperation<'ring, 'buf>>,
dependencies: HashMap<usize, Vec<usize>>, max_operations: usize,
}
#[derive(Debug)]
struct BatchOperation<'ring, 'buf> {
operation: Operation<'ring, 'buf, Building>,
}
impl<'ring, 'buf> Batch<'ring, 'buf> {
pub fn new() -> Self {
Self::with_capacity(16) }
pub fn with_capacity(capacity: usize) -> Self {
Self {
operations: Vec::with_capacity(capacity),
dependencies: HashMap::new(),
max_operations: 1024, }
}
pub fn add_operation(&mut self, operation: Operation<'ring, 'buf, Building>) -> Result<usize> {
if self.operations.len() >= self.max_operations {
return Err(SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Batch is full (max {} operations)", self.max_operations),
)));
}
operation.validate().map_err(|msg| {
SaferRingError::Io(std::io::Error::new(std::io::ErrorKind::InvalidInput, msg))
})?;
let index = self.operations.len();
self.operations.push(BatchOperation { operation });
Ok(index)
}
pub fn add_operation_with_data(
&mut self,
operation: Operation<'ring, 'buf, Building>,
_user_data: u64,
) -> Result<usize> {
if self.operations.len() >= self.max_operations {
return Err(SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Batch is full (max {} operations)", self.max_operations),
)));
}
operation.validate().map_err(|msg| {
SaferRingError::Io(std::io::Error::new(std::io::ErrorKind::InvalidInput, msg))
})?;
let index = self.operations.len();
self.operations.push(BatchOperation { operation });
Ok(index)
}
pub fn add_dependency(&mut self, dependent: usize, dependency: usize) -> Result<()> {
if dependent >= self.operations.len() {
return Err(SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Dependent operation index {dependent} is out of bounds"),
)));
}
if dependency >= self.operations.len() {
return Err(SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Dependency operation index {dependency} is out of bounds"),
)));
}
if dependent == dependency {
return Err(SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Operation cannot depend on itself",
)));
}
if DependencyValidator::would_create_cycle(&self.dependencies, dependent, dependency) {
return Err(SaferRingError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Dependency would create a cycle",
)));
}
self.dependencies
.entry(dependent)
.or_default()
.push(dependency);
Ok(())
}
pub fn len(&self) -> usize {
self.operations.len()
}
pub fn is_empty(&self) -> bool {
self.operations.is_empty()
}
pub fn max_operations(&self) -> usize {
self.max_operations
}
pub fn has_circular_dependencies(&self) -> bool {
DependencyValidator::has_circular_dependencies(&self.dependencies)
}
pub fn clear(&mut self) {
self.operations.clear();
self.dependencies.clear();
}
pub(crate) fn dependency_order(&self) -> Result<Vec<usize>> {
DependencyValidator::dependency_order(&self.dependencies, self.operations.len())
}
pub(crate) fn into_operations_and_dependencies(
self,
) -> (
Vec<Operation<'ring, 'buf, Building>>,
HashMap<usize, Vec<usize>>,
) {
let operations = self
.operations
.into_iter()
.map(|batch_op| batch_op.operation)
.collect();
(operations, self.dependencies)
}
}
impl<'ring, 'buf> Default for Batch<'ring, 'buf> {
fn default() -> Self {
Self::new()
}
}