use std::sync::{Arc, RwLock};
use foyer_common::error::{Error, ErrorKind, Result};
use crate::{
io::device::{Device, DeviceBuilder, Partition, PartitionId},
RawFile, Statistics, Throttle,
};
#[derive(Debug)]
pub struct CombinedDeviceBuilder {
devices: Vec<Arc<dyn Device>>,
throttle: Throttle,
}
impl Default for CombinedDeviceBuilder {
fn default() -> Self {
Self::new()
}
}
impl CombinedDeviceBuilder {
pub fn new() -> Self {
Self {
devices: vec![],
throttle: Throttle::default(),
}
}
pub fn with_device(mut self, device: Arc<dyn Device>) -> Self {
self.devices.push(device);
self
}
pub fn with_throttle(mut self, throttle: Throttle) -> Self {
self.throttle = throttle;
self
}
}
impl DeviceBuilder for CombinedDeviceBuilder {
fn build(self) -> Result<Arc<dyn Device>> {
let device = CombinedDevice {
devices: self.devices,
statistics: Arc::new(Statistics::new(self.throttle)),
inner: RwLock::new(Inner {
partitions: vec![],
next: 0,
}),
};
let device = Arc::new(device);
Ok(device)
}
}
#[derive(Debug)]
struct Inner {
partitions: Vec<Arc<CombinedPartition>>,
next: usize,
}
#[derive(Debug)]
pub struct CombinedDevice {
devices: Vec<Arc<dyn Device>>,
inner: RwLock<Inner>,
statistics: Arc<Statistics>,
}
impl Device for CombinedDevice {
fn capacity(&self) -> usize {
self.devices.iter().map(|d| d.capacity()).sum()
}
fn allocated(&self) -> usize {
let inner = self.inner.read().unwrap();
let allocated = inner.partitions.iter().take(inner.next).map(|p| p.size()).sum();
if inner.next < inner.partitions.len() {
allocated + self.devices[inner.next].allocated()
} else {
allocated
}
}
fn create_partition(&self, size: usize) -> Result<Arc<dyn Partition>> {
let mut inner = self.inner.write().unwrap();
loop {
if inner.next >= self.devices.len() {
let capacity = self.devices.iter().map(|d| d.capacity()).sum::<usize>();
return Err(Error::no_space(capacity, capacity, size));
}
let device = &self.devices[inner.next];
match device.create_partition(size) {
Ok(p) => {
let partition = CombinedPartition {
inner: p,
id: inner.partitions.len() as PartitionId,
statistics: self.statistics.clone(),
};
let partition = Arc::new(partition);
inner.partitions.push(partition.clone());
return Ok(partition);
}
Err(e) => {
if e.kind() == ErrorKind::NoSpace {
inner.next += 1;
continue;
}
return Err(e);
}
}
}
}
fn partitions(&self) -> usize {
self.inner.read().unwrap().partitions.len()
}
fn partition(&self, id: PartitionId) -> Arc<dyn Partition> {
self.inner.read().unwrap().partitions[id as usize].clone()
}
fn statistics(&self) -> &Arc<Statistics> {
&self.statistics
}
}
#[derive(Debug)]
pub struct CombinedPartition {
inner: Arc<dyn Partition>,
id: PartitionId,
statistics: Arc<Statistics>,
}
impl Partition for CombinedPartition {
fn id(&self) -> PartitionId {
self.id
}
fn size(&self) -> usize {
self.inner.size()
}
fn translate(&self, address: u64) -> (RawFile, u64) {
self.inner.translate(address)
}
fn statistics(&self) -> &Arc<Statistics> {
&self.statistics
}
}