use std::{fmt::Debug, path::PathBuf};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use super::meta::{ActorError, ActorResult};
use crate::store::util::SliceInfoExt;
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum DataLocation<I = (), E = ()> {
Inline(I),
Owned(E),
External(Vec<PathBuf>, E),
}
impl<I: AsRef<[u8]>, E: Debug> DataLocation<I, E> {
fn fmt_short(&self) -> String {
match self {
DataLocation::Inline(d) => {
format!("Inline({}, addr={})", d.as_ref().len(), d.addr_short())
}
DataLocation::Owned(e) => format!("Owned({e:?})"),
DataLocation::External(paths, e) => {
let paths = paths.iter().map(|p| p.display()).collect::<Vec<_>>();
format!("External({paths:?}, {e:?})")
}
}
}
}
impl<X> DataLocation<X, u64> {
#[allow(clippy::result_large_err)]
fn union(self, that: DataLocation<X, u64>) -> ActorResult<Self> {
Ok(match (self, that) {
(
DataLocation::External(mut paths, a_size),
DataLocation::External(b_paths, b_size),
) => {
if a_size != b_size {
return Err(ActorError::inconsistent(format!(
"complete size mismatch {a_size} {b_size}"
)));
}
paths.extend(b_paths);
paths.sort();
paths.dedup();
DataLocation::External(paths, a_size)
}
(_, b @ DataLocation::Owned(_)) => {
b
}
(a @ DataLocation::Owned(_), _) => {
a
}
(_, b @ DataLocation::Inline(_)) => {
b
}
(a @ DataLocation::Inline(_), _) => {
a
}
})
}
}
impl<I, E> DataLocation<I, E> {
#[allow(dead_code)]
pub fn discard_inline_data(self) -> DataLocation<(), E> {
match self {
DataLocation::Inline(_) => DataLocation::Inline(()),
DataLocation::Owned(x) => DataLocation::Owned(x),
DataLocation::External(paths, x) => DataLocation::External(paths, x),
}
}
pub fn split_inline_data(self) -> (DataLocation<(), E>, Option<I>) {
match self {
DataLocation::Inline(x) => (DataLocation::Inline(()), Some(x)),
DataLocation::Owned(x) => (DataLocation::Owned(x), None),
DataLocation::External(paths, x) => (DataLocation::External(paths, x), None),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum OutboardLocation<I = ()> {
Inline(I),
Owned,
NotNeeded,
}
impl<I: AsRef<[u8]>> OutboardLocation<I> {
fn fmt_short(&self) -> String {
match self {
OutboardLocation::Inline(d) => format!("Inline({})", d.as_ref().len()),
OutboardLocation::Owned => "Owned".to_string(),
OutboardLocation::NotNeeded => "NotNeeded".to_string(),
}
}
}
impl<I> OutboardLocation<I> {
pub fn inline(data: I) -> Self
where
I: AsRef<[u8]>,
{
if data.as_ref().is_empty() {
OutboardLocation::NotNeeded
} else {
OutboardLocation::Inline(data)
}
}
#[allow(dead_code)]
pub fn discard_extra_data(self) -> OutboardLocation<()> {
match self {
Self::Inline(_) => OutboardLocation::Inline(()),
Self::Owned => OutboardLocation::Owned,
Self::NotNeeded => OutboardLocation::NotNeeded,
}
}
pub fn split_inline_data(self) -> (OutboardLocation<()>, Option<I>) {
match self {
Self::Inline(x) => (OutboardLocation::Inline(()), Some(x)),
Self::Owned => (OutboardLocation::Owned, None),
Self::NotNeeded => (OutboardLocation::NotNeeded, None),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum EntryState<I = ()> {
Complete {
data_location: DataLocation<I, u64>,
outboard_location: OutboardLocation<I>,
},
Partial {
size: Option<u64>,
},
}
impl<I> EntryState<I> {
pub fn is_complete(&self) -> bool {
matches!(self, Self::Complete { .. })
}
pub fn is_partial(&self) -> bool {
matches!(self, Self::Partial { .. })
}
}
impl Default for EntryState {
fn default() -> Self {
Self::Partial { size: None }
}
}
impl<I: AsRef<[u8]>> EntryState<I> {
pub fn fmt_short(&self) -> String {
match self {
Self::Complete {
data_location,
outboard_location,
} => format!(
"Complete {{ data: {}, outboard: {} }}",
data_location.fmt_short(),
outboard_location.fmt_short()
),
Self::Partial { size } => format!("Partial {{ size: {size:?} }}"),
}
}
}
impl EntryState {
#[allow(clippy::result_large_err)]
pub fn union(old: Self, new: Self) -> ActorResult<Self> {
match (old, new) {
(
Self::Complete {
data_location,
outboard_location,
},
Self::Complete {
data_location: b_data_location,
..
},
) => Ok(Self::Complete {
data_location: data_location.union(b_data_location)?,
outboard_location,
}),
(a @ Self::Complete { .. }, Self::Partial { .. }) =>
{
Ok(a)
}
(Self::Partial { .. }, b @ Self::Complete { .. }) =>
{
Ok(b)
}
(Self::Partial { size: a_size }, Self::Partial { size: b_size }) =>
{
let size = match (a_size, b_size) {
(Some(a_size), Some(b_size)) => {
if a_size != b_size {
return Err(ActorError::inconsistent(format!(
"validated size mismatch {a_size} {b_size}"
)));
}
Some(a_size)
}
(Some(a_size), None) => Some(a_size),
(None, Some(b_size)) => Some(b_size),
(None, None) => None,
};
Ok(Self::Partial { size })
}
}
}
}
impl redb::Value for EntryState {
type SelfType<'a> = EntryState;
type AsBytes<'a> = SmallVec<[u8; 128]>;
fn fixed_width() -> Option<usize> {
None
}
fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
where
Self: 'a,
{
postcard::from_bytes(data).unwrap()
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
where
Self: 'a,
Self: 'b,
{
postcard::to_extend(value, SmallVec::new()).unwrap()
}
fn type_name() -> redb::TypeName {
redb::TypeName::new("EntryState")
}
}