1use super::{CacheKey, RandomAccessFileReader, ZstdFrameCache};
12use crate::blocks::{Tipset, TipsetKey};
13use crate::chain::FilecoinSnapshotMetadata;
14use crate::prelude::*;
15use crate::utils::db::car_stream::CarV1Header;
16use crate::utils::io::EitherMmapOrRandomAccessFile;
17use itertools::Either;
18use positioned_io::ReadAt;
19use std::{
20 borrow::Cow,
21 io::{Error, ErrorKind, Read, Result},
22 path::{Path, PathBuf},
23};
24
25#[derive(derive_more::From)]
26pub enum AnyCar<ReaderT> {
27 Plain(super::PlainCar<ReaderT>),
28 Forest(super::ForestCar<ReaderT>),
29 #[from(skip)]
30 Memory(super::PlainCar<Vec<u8>>),
31}
32
33impl<ReaderT: RandomAccessFileReader> AnyCar<ReaderT> {
34 pub fn new(reader: ReaderT) -> Result<Self> {
38 if let Ok(validation_result) = super::ForestCar::validate_car(&reader) {
39 return Ok(
40 super::ForestCar::new_from_validation_result(reader, validation_result)?.into(),
41 );
42 }
43
44 if let Ok(decompressed) = zstd::stream::decode_all(positioned_io::Cursor::new(&reader))
46 && let Ok(mem_car) = super::PlainCar::new(decompressed)
47 {
48 return Ok(AnyCar::Memory(mem_car));
49 }
50
51 if let Ok(plain_car) = super::PlainCar::new(reader) {
52 return Ok(plain_car.into());
53 }
54 Err(Error::new(
55 ErrorKind::InvalidData,
56 "input not recognized as any kind of CAR data (.car, .car.zst, .forest.car)",
57 ))
58 }
59
60 pub fn header_v1(&self) -> &CarV1Header {
61 match self {
62 AnyCar::Forest(forest) => forest.header_v1(),
63 AnyCar::Plain(plain) => plain.header_v1(),
64 AnyCar::Memory(mem) => mem.header_v1(),
65 }
66 }
67
68 pub fn metadata(&self) -> Option<&FilecoinSnapshotMetadata> {
69 match self {
70 AnyCar::Forest(forest) => forest.metadata(),
71 AnyCar::Plain(plain) => plain.metadata(),
72 AnyCar::Memory(mem) => mem.metadata(),
73 }
74 }
75
76 pub fn heaviest_tipset_key(&self) -> TipsetKey {
77 match self {
78 AnyCar::Forest(forest) => forest.heaviest_tipset_key(),
79 AnyCar::Plain(plain) => plain.heaviest_tipset_key(),
80 AnyCar::Memory(mem) => mem.heaviest_tipset_key(),
81 }
82 }
83
84 pub fn heaviest_tipset(&self) -> anyhow::Result<Tipset> {
87 match self {
88 AnyCar::Forest(forest) => forest.heaviest_tipset(),
89 AnyCar::Plain(plain) => plain.heaviest_tipset(),
90 AnyCar::Memory(mem) => mem.heaviest_tipset(),
91 }
92 }
93
94 pub fn variant(&self) -> Cow<'static, str> {
97 match self {
98 AnyCar::Forest(_) => "ForestCARv1.zst".into(),
99 AnyCar::Plain(car) => format!("CARv{}", car.version()).into(),
100 AnyCar::Memory(car) => format!("CARv{}.zst", car.version()).into(),
101 }
102 }
103
104 pub fn into_dyn(self) -> AnyCar<Box<dyn super::RandomAccessFileReader>> {
106 match self {
107 AnyCar::Forest(f) => AnyCar::Forest(f.into_dyn()),
108 AnyCar::Plain(p) => AnyCar::Plain(p.into_dyn()),
109 AnyCar::Memory(m) => AnyCar::Memory(m),
110 }
111 }
112
113 pub fn with_cache(self, cache: ZstdFrameCache, key: CacheKey) -> Self {
115 match self {
116 AnyCar::Forest(f) => AnyCar::Forest(f.with_cache(cache, key)),
117 AnyCar::Plain(p) => AnyCar::Plain(p),
118 AnyCar::Memory(m) => AnyCar::Memory(m),
119 }
120 }
121
122 pub fn index_size_bytes(&self) -> Option<u64> {
124 match self {
125 Self::Forest(car) => Some(car.index_size_bytes()),
126 _ => None,
127 }
128 }
129
130 pub fn get_reader(&self, k: Cid) -> anyhow::Result<Option<impl Read>> {
132 match self {
133 Self::Forest(car) => Ok(car.get_reader(k)?.map(Either::Left)),
134 Self::Plain(car) => Ok(car.get_reader(k).map(|r| Either::Right(Either::Left(r)))),
135 Self::Memory(car) => Ok(car.get_reader(k).map(|r| Either::Right(Either::Right(r)))),
136 }
137 }
138}
139
140impl TryFrom<&'static [u8]> for AnyCar<&'static [u8]> {
141 type Error = std::io::Error;
142 fn try_from(bytes: &'static [u8]) -> std::io::Result<Self> {
143 Ok(AnyCar::Plain(super::PlainCar::new(bytes)?))
144 }
145}
146
147impl TryFrom<&Path> for AnyCar<EitherMmapOrRandomAccessFile> {
148 type Error = std::io::Error;
149 fn try_from(path: &Path) -> std::io::Result<Self> {
150 AnyCar::new(EitherMmapOrRandomAccessFile::open(path)?)
151 }
152}
153
154impl TryFrom<&PathBuf> for AnyCar<EitherMmapOrRandomAccessFile> {
155 type Error = std::io::Error;
156 fn try_from(path: &PathBuf) -> std::io::Result<Self> {
157 Self::try_from(path.as_path())
158 }
159}
160
161impl<ReaderT> Blockstore for AnyCar<ReaderT>
162where
163 ReaderT: ReadAt,
164{
165 fn get(&self, k: &Cid) -> anyhow::Result<Option<Vec<u8>>> {
166 match self {
167 AnyCar::Forest(forest) => forest.get(k),
168 AnyCar::Plain(plain) => plain.get(k),
169 AnyCar::Memory(mem) => mem.get(k),
170 }
171 }
172
173 fn put_keyed(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()> {
174 match self {
175 AnyCar::Forest(forest) => forest.put_keyed(k, block),
176 AnyCar::Plain(plain) => plain.put_keyed(k, block),
177 AnyCar::Memory(mem) => mem.put_keyed(k, block),
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::networks::{calibnet, mainnet};
186
187 #[test]
188 fn forest_any_load_calibnet() {
189 let forest_car = AnyCar::new(calibnet::DEFAULT_GENESIS).unwrap();
190 assert!(forest_car.has(&calibnet::GENESIS_CID).unwrap());
191 }
192
193 #[test]
194 fn forest_any_load_calibnet_zstd() {
195 let data = zstd::encode_all(calibnet::DEFAULT_GENESIS, 3).unwrap();
196 let forest_car = AnyCar::new(data).unwrap();
197 assert!(forest_car.has(&calibnet::GENESIS_CID).unwrap());
198 }
199
200 #[test]
201 fn forest_any_load_mainnet() {
202 let forest_car = AnyCar::new(mainnet::DEFAULT_GENESIS).unwrap();
203 assert!(forest_car.has(&mainnet::GENESIS_CID).unwrap());
204 }
205
206 #[test]
207 fn forest_any_load_mainnet_zstd() {
208 let data = zstd::encode_all(mainnet::DEFAULT_GENESIS, 3).unwrap();
209 let forest_car = AnyCar::new(data).unwrap();
210 assert!(forest_car.has(&mainnet::GENESIS_CID).unwrap());
211 }
212}