Skip to main content

co_storage/storage/
static_storage.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 1io BRANDGUARDIAN GmbH
3
4use crate::{ExtendedBlock, ExtendedBlockStorage};
5use async_trait::async_trait;
6use cid::Cid;
7use co_primitives::{Block, BlockStat, BlockStorage, KnownMultiCodec, StorageError};
8use std::{collections::BTreeMap, ops::Range};
9
10/// Block storage implementation for static data.
11#[derive(Debug, Clone)]
12pub struct StaticBlockStorage<'a> {
13	data: &'a [u8],
14	blocks: BTreeMap<Cid, StaticBlock>,
15}
16impl<'a> StaticBlockStorage<'a> {
17	/// Static block storage builder.
18	pub fn builder(data: &'a [u8]) -> StaticBlockStorageBuilder<'a> {
19		StaticBlockStorageBuilder::new(data)
20	}
21
22	/// Create static block storage from plain block.
23	pub fn new_unchecked(cid: Cid, data: &'a [u8]) -> Self {
24		Self { blocks: [(cid, StaticBlock::Range(0..data.len()))].into_iter().collect(), data }
25	}
26
27	/// Create static block storage from raw block.
28	pub fn new_raw(data: &'a [u8]) -> (Cid, Self) {
29		let cid = Block::cid_data(KnownMultiCodec::Raw, data);
30		let storage = Self { blocks: [(cid, StaticBlock::Range(0..data.len()))].into_iter().collect(), data };
31		(cid, storage)
32	}
33}
34#[async_trait]
35impl<'a> BlockStorage for StaticBlockStorage<'a> {
36	async fn get(&self, cid: &Cid) -> Result<Block, StorageError> {
37		let block = self
38			.blocks
39			.get(cid)
40			.ok_or_else(|| StorageError::NotFound(*cid, anyhow::anyhow!("Block not found")))?;
41		Ok(Block::new_unchecked(*cid, block.to_vec(self.data)))
42	}
43
44	async fn set(&self, _block: Block) -> Result<Cid, StorageError> {
45		Err(StorageError::InvalidArgument(anyhow::anyhow!("Readonly storage")))
46	}
47
48	async fn stat(&self, cid: &Cid) -> Result<BlockStat, StorageError> {
49		let block = self
50			.blocks
51			.get(cid)
52			.ok_or_else(|| StorageError::NotFound(*cid, anyhow::anyhow!("Block not found")))?;
53		Ok(BlockStat { size: block.len() as u64 })
54	}
55
56	async fn remove(&self, _cid: &Cid) -> Result<(), StorageError> {
57		Err(StorageError::InvalidArgument(anyhow::anyhow!("Readonly storage")))
58	}
59
60	fn max_block_size(&self) -> usize {
61		0
62	}
63}
64#[async_trait]
65impl<'a> ExtendedBlockStorage for StaticBlockStorage<'a> {
66	async fn set_extended(&self, _block: ExtendedBlock) -> Result<Cid, StorageError> {
67		Err(StorageError::InvalidArgument(anyhow::anyhow!("Readonly storage")))
68	}
69
70	async fn exists(&self, cid: &Cid) -> Result<bool, StorageError> {
71		Ok(self.blocks.contains_key(cid))
72	}
73
74	async fn clear(&self) -> Result<(), StorageError> {
75		Err(StorageError::InvalidArgument(anyhow::anyhow!("Readonly storage")))
76	}
77}
78
79#[derive(Debug, Clone)]
80enum StaticBlock {
81	Range(Range<usize>),
82	Data(Vec<u8>),
83}
84impl StaticBlock {
85	pub fn to_vec(&self, data: &[u8]) -> Vec<u8> {
86		match self {
87			Self::Range(range) => data[range.start..range.end].to_vec(),
88			Self::Data(data) => data.clone(),
89		}
90	}
91
92	pub fn len(&self) -> usize {
93		match self {
94			StaticBlock::Range(range) => range.len(),
95			StaticBlock::Data(data) => data.len(),
96		}
97	}
98}
99
100pub struct StaticBlockStorageBuilder<'a> {
101	data: &'a [u8],
102	blocks: BTreeMap<Cid, StaticBlock>,
103}
104impl<'a> StaticBlockStorageBuilder<'a> {
105	pub fn new(data: &'a [u8]) -> Self {
106		Self { data, blocks: Default::default() }
107	}
108
109	pub fn with_range(mut self, cid: Cid, range: Range<usize>) -> Self {
110		self.blocks.insert(cid, StaticBlock::Range(range));
111		self
112	}
113
114	pub fn with_block(mut self, cid: Cid, data: Vec<u8>) -> Self {
115		self.blocks.insert(cid, StaticBlock::Data(data));
116		self
117	}
118
119	pub fn build(self) -> StaticBlockStorage<'a> {
120		StaticBlockStorage { blocks: self.blocks, data: self.data }
121	}
122}