Skip to main content

skippy_cache/payload/
mod.rs

1use std::{borrow::Cow, fmt};
2
3use anyhow::{Result, anyhow};
4
5mod blob_store;
6pub(super) mod bytes;
7
8pub use blob_store::{CacheBlobStore, CacheDedupeStats};
9pub use bytes::{CacheBytes, CacheBytesReconstructStats};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ExactStatePayloadKind {
13    FullState,
14    RecurrentOnly,
15    KvRecurrent,
16}
17
18#[derive(Debug, Clone)]
19pub enum ExactStatePayload {
20    FullState {
21        bytes: CacheBytes,
22    },
23    RecurrentOnly {
24        recurrent: CacheBytes,
25    },
26    KvRecurrent {
27        kv: CacheBytes,
28        recurrent: CacheBytes,
29    },
30}
31
32impl ExactStatePayload {
33    pub fn full_state(bytes: Vec<u8>) -> Self {
34        Self::FullState {
35            bytes: CacheBytes::inline(bytes),
36        }
37    }
38
39    pub fn recurrent_only(recurrent: Vec<u8>) -> Self {
40        Self::RecurrentOnly {
41            recurrent: CacheBytes::inline(recurrent),
42        }
43    }
44
45    pub fn kv_recurrent(kv: Vec<u8>, recurrent: Vec<u8>) -> Self {
46        Self::KvRecurrent {
47            kv: CacheBytes::inline(kv),
48            recurrent: CacheBytes::inline(recurrent),
49        }
50    }
51
52    pub fn kind(&self) -> ExactStatePayloadKind {
53        match self {
54            Self::FullState { .. } => ExactStatePayloadKind::FullState,
55            Self::RecurrentOnly { .. } => ExactStatePayloadKind::RecurrentOnly,
56            Self::KvRecurrent { .. } => ExactStatePayloadKind::KvRecurrent,
57        }
58    }
59
60    pub fn byte_len(&self) -> u64 {
61        match self {
62            Self::FullState { bytes } => bytes.len(),
63            Self::RecurrentOnly { recurrent } => recurrent.len(),
64            Self::KvRecurrent { kv, recurrent } => kv.len().saturating_add(recurrent.len()),
65        }
66    }
67
68    pub fn recurrent_state_bytes(&self) -> Result<Cow<'_, [u8]>> {
69        match self {
70            Self::RecurrentOnly { recurrent } | Self::KvRecurrent { recurrent, .. } => {
71                recurrent.as_cow()
72            }
73            _ => Err(anyhow!("cache payload has no recurrent component")),
74        }
75    }
76
77    pub fn recurrent_state_bytes_timed(
78        &self,
79    ) -> Result<(Cow<'_, [u8]>, CacheBytesReconstructStats)> {
80        match self {
81            Self::RecurrentOnly { recurrent } | Self::KvRecurrent { recurrent, .. } => {
82                recurrent.as_cow_timed()
83            }
84            _ => Err(anyhow!("cache payload has no recurrent component")),
85        }
86    }
87
88    pub fn full_state_bytes_timed(&self) -> Result<(Cow<'_, [u8]>, CacheBytesReconstructStats)> {
89        match self {
90            Self::FullState { bytes } => bytes.as_cow_timed(),
91            _ => Err(anyhow!("cache payload is not full-state")),
92        }
93    }
94
95    pub fn kv_bytes(&self) -> Result<Option<Cow<'_, [u8]>>> {
96        match self {
97            Self::KvRecurrent { kv, .. } => Ok(Some(kv.as_cow()?)),
98            _ => Ok(None),
99        }
100    }
101
102    pub fn kv_bytes_timed(&self) -> Result<Option<(Cow<'_, [u8]>, CacheBytesReconstructStats)>> {
103        match self {
104            Self::KvRecurrent { kv, .. } => Ok(Some(kv.as_cow_timed()?)),
105            _ => Ok(None),
106        }
107    }
108
109    pub fn dedupe_into(self, blobs: &mut CacheBlobStore) -> (Self, CacheDedupeStats) {
110        match self {
111            Self::FullState { bytes } => {
112                let (bytes, stats) = blobs.store_bytes(bytes);
113                (Self::FullState { bytes }, stats)
114            }
115            Self::RecurrentOnly { recurrent } => {
116                let (recurrent, stats) = blobs.store_bytes(recurrent);
117                (Self::RecurrentOnly { recurrent }, stats)
118            }
119            Self::KvRecurrent { kv, recurrent } => {
120                let (kv, kv_stats) = blobs.store_bytes(kv);
121                let (recurrent, recurrent_stats) = blobs.store_bytes(recurrent);
122                (
123                    Self::KvRecurrent { kv, recurrent },
124                    kv_stats.saturating_add(recurrent_stats),
125                )
126            }
127        }
128    }
129
130    pub fn release_from(&self, blobs: &mut CacheBlobStore) {
131        match self {
132            Self::FullState { bytes } => blobs.release_bytes(bytes),
133            Self::RecurrentOnly { recurrent } => blobs.release_bytes(recurrent),
134            Self::KvRecurrent { kv, recurrent } => {
135                blobs.release_bytes(kv);
136                blobs.release_bytes(recurrent);
137            }
138        }
139    }
140}
141
142impl fmt::Display for ExactStatePayloadKind {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        match self {
145            Self::FullState => f.write_str("full-state"),
146            Self::RecurrentOnly => f.write_str("recurrent-only"),
147            Self::KvRecurrent => f.write_str("kv-recurrent"),
148        }
149    }
150}