1use std::collections::HashSet;
2use std::rc::Rc;
5
6use cid::Cid;
7use fvm_ipld_encoding::ipld_block::IpldBlock;
8
9use super::Result;
10use crate::syscall_error;
11
12#[derive(Default)]
15pub struct BlockRegistry {
16 blocks: Vec<Block>,
17 reachable: HashSet<Cid>,
18}
19
20pub type BlockId = u32;
24
25const FIRST_ID: BlockId = 1;
26const MAX_BLOCKS: u32 = i32::MAX as u32; #[derive(Debug, Copy, Clone)]
29pub struct BlockStat {
30 pub codec: u64,
31 pub size: u32,
32}
33
34#[derive(Debug, Clone)]
35pub struct Block(Rc<BlockInner>);
36#[derive(Debug)]
37struct BlockInner {
38 codec: u64,
39 data: Box<[u8]>,
40 links: Box<[Cid]>,
41}
42
43impl Block {
44 pub fn new(codec: u64, data: impl Into<Box<[u8]>>, links: impl Into<Box<[Cid]>>) -> Self {
45 Self(Rc::new(BlockInner {
48 codec,
49 data: data.into(),
50 links: links.into(),
51 }))
52 }
53
54 #[inline(always)]
55 pub fn codec(&self) -> u64 {
56 self.0.codec
57 }
58
59 #[inline(always)]
60 pub fn links(&self) -> &[Cid] {
61 &self.0.links
62 }
63
64 #[inline(always)]
65 pub fn data(&self) -> &[u8] {
66 &self.0.data
67 }
68
69 #[inline(always)]
70 pub fn size(&self) -> u32 {
71 self.0.data.len() as u32
72 }
73
74 #[inline(always)]
75 pub fn stat(&self) -> BlockStat {
76 BlockStat {
77 codec: self.codec(),
78 size: self.size(),
79 }
80 }
81}
82
83impl From<&Block> for IpldBlock {
84 fn from(b: &Block) -> Self {
85 IpldBlock {
86 codec: b.0.codec,
87 data: Vec::from(&*b.0.data),
88 }
89 }
90}
91
92impl BlockRegistry {
93 pub(crate) fn new() -> Self {
94 Self::default()
95 }
96}
97
98impl BlockRegistry {
99 pub fn put_reachable(&mut self, block: Block) -> Result<BlockId> {
102 self.put_inner(block, false)
103 }
104
105 pub fn put_check_reachable(&mut self, block: Block) -> Result<BlockId> {
110 self.put_inner(block, true)
111 }
112
113 pub fn mark_reachable(&mut self, k: &Cid) {
115 self.reachable.insert(*k);
116 }
117
118 pub fn is_reachable(&self, k: &Cid) -> bool {
121 self.reachable.contains(k)
124 }
125
126 fn put_inner(&mut self, block: Block, check_reachable: bool) -> Result<BlockId> {
128 if self.is_full() {
129 return Err(syscall_error!(LimitExceeded; "too many blocks").into());
130 }
131
132 if check_reachable {
134 if let Some(k) = block.links().iter().find(|k| !self.is_reachable(k)) {
135 return Err(syscall_error!(NotFound; "cannot put block: {k} not reachable").into());
136 }
137 } else {
138 for k in block.links() {
139 self.mark_reachable(k)
140 }
141 }
142
143 let id = FIRST_ID + self.blocks.len() as u32;
144 self.blocks.push(block);
145 Ok(id)
146 }
147
148 pub fn get(&self, id: BlockId) -> Result<&Block> {
150 if id < FIRST_ID {
151 return Err(syscall_error!(InvalidHandle; "invalid block handle {id}").into());
152 }
153 id.try_into()
154 .ok()
155 .and_then(|idx: usize| self.blocks.get(idx - FIRST_ID as usize))
156 .ok_or(syscall_error!(InvalidHandle; "invalid block handle {id}").into())
157 }
158
159 pub fn stat(&self, id: BlockId) -> Result<BlockStat> {
161 if id < FIRST_ID {
162 return Err(syscall_error!(InvalidHandle; "invalid block handle {id}").into());
163 }
164 id.try_into()
165 .ok()
166 .and_then(|idx: usize| self.blocks.get(idx - FIRST_ID as usize))
167 .ok_or(syscall_error!(InvalidHandle; "invalid block handle {id}").into())
168 .map(|b| BlockStat {
169 codec: b.codec(),
170 size: b.size(),
171 })
172 }
173
174 pub fn is_full(&self) -> bool {
175 self.blocks.len() as u32 == MAX_BLOCKS
176 }
177}