use std::{fmt::Debug, future::Future, marker::PhantomData, pin::Pin, sync::Arc};
use foyer_common::{
code::{Key, Value},
properties::Properties,
};
use crate::{record::Record, Eviction};
pub struct Piece<K, V, P> {
record: *const (),
key: *const K,
value: *const V,
hash: u64,
properties: *const P,
drop_fn: fn(*const ()),
}
impl<K, V, P> Debug for Piece<K, V, P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Piece")
.field("record", &self.record)
.field("hash", &self.hash)
.finish()
}
}
unsafe impl<K, V, P> Send for Piece<K, V, P>
where
K: Key,
V: Value,
P: Properties,
{
}
unsafe impl<K, V, P> Sync for Piece<K, V, P>
where
K: Key,
V: Value,
P: Properties,
{
}
impl<K, V, P> Drop for Piece<K, V, P> {
fn drop(&mut self) {
(self.drop_fn)(self.record);
}
}
impl<K, V, P> Clone for Piece<K, V, P> {
fn clone(&self) -> Self {
unsafe { Arc::increment_strong_count(self.record) };
Self {
record: self.record,
key: self.key,
value: self.value,
hash: self.hash,
properties: self.properties,
drop_fn: self.drop_fn,
}
}
}
impl<K, V, P> Piece<K, V, P> {
pub fn new<E>(record: Arc<Record<E>>) -> Self
where
E: Eviction<Key = K, Value = V, Properties = P>,
{
let raw = Arc::into_raw(record);
let record = raw as *const ();
let key = unsafe { (*raw).key() } as *const _;
let value = unsafe { (*raw).value() } as *const _;
let hash = unsafe { (*raw).hash() };
let properties = unsafe { (*raw).properties() } as *const _;
let drop_fn = |ptr| unsafe {
let _ = Arc::from_raw(ptr as *const Record<E>);
};
Self {
record,
key,
value,
hash,
properties,
drop_fn,
}
}
pub fn key(&self) -> &K {
unsafe { &*self.key }
}
pub fn value(&self) -> &V {
unsafe { &*self.value }
}
pub fn hash(&self) -> u64 {
self.hash
}
pub fn properties(&self) -> &P {
unsafe { &*self.properties }
}
pub(crate) fn into_record<E>(mut self) -> Arc<Record<E>>
where
E: Eviction<Key = K, Value = V, Properties = P>,
{
self.drop_fn = |_| {};
let record = self.record as *const Record<E>;
unsafe { Arc::from_raw(record) }
}
}
pub type ArcPipe<K, V, P> = Arc<dyn Pipe<Key = K, Value = V, Properties = P>>;
pub trait Pipe: Send + Sync + 'static + Debug {
type Key;
type Value;
type Properties;
fn is_enabled(&self) -> bool;
fn send(&self, piece: Piece<Self::Key, Self::Value, Self::Properties>);
fn flush(
&self,
pieces: Vec<Piece<Self::Key, Self::Value, Self::Properties>>,
) -> Pin<Box<dyn Future<Output = ()> + Send>>;
}
pub struct NoopPipe<K, V, P>(PhantomData<(K, V, P)>);
impl<K, V, P> Debug for NoopPipe<K, V, P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("NoopPipe").finish()
}
}
impl<K, V, P> Default for NoopPipe<K, V, P> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<K, V, P> Pipe for NoopPipe<K, V, P>
where
K: Key,
V: Value,
P: Properties,
{
type Key = K;
type Value = V;
type Properties = P;
fn is_enabled(&self) -> bool {
false
}
fn send(&self, _: Piece<Self::Key, Self::Value, Self::Properties>) {}
fn flush(
&self,
_: Vec<Piece<Self::Key, Self::Value, Self::Properties>>,
) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(std::future::ready(()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
eviction::{fifo::Fifo, test_utils::TestProperties},
record::Data,
};
#[test]
fn test_piece() {
let r1 = Arc::new(Record::new(Data::<Fifo<Arc<Vec<u8>>, Arc<Vec<u8>>, TestProperties>> {
key: Arc::new(vec![b'k'; 4096]),
value: Arc::new(vec![b'k'; 16384]),
properties: TestProperties::default(),
hash: 1,
weight: 1,
}));
let p1 = Piece::new(r1.clone());
let k1 = p1.key().clone();
let r2 = r1.clone();
let p2 = Piece::new(r1.clone());
drop(p1);
drop(r2);
drop(p2);
drop(r1);
drop(k1);
}
}