use std::time::Instant;
use ipfs_sqlite_block_store::{Block, BlockStore, Config, OwnedBlock};
use libipld::{cbor::DagCborCodec, cid::Cid, codec::Codec, DagCbor};
use multihash::{Code, MultihashDigest};
use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter};
#[derive(Debug, DagCbor)]
struct Node {
links: Vec<Cid>,
text: String,
}
impl Node {
pub fn branch(text: &str, links: impl IntoIterator<Item = Cid>) -> Self {
Self {
links: links.into_iter().collect(),
text: text.into(),
}
}
}
fn block(name: &str, links: impl IntoIterator<Item = Cid>) -> OwnedBlock {
let links = links.into_iter().collect::<Vec<_>>();
let data_size = if links.is_empty() {
1024 * 16 - 16
} else {
512
};
let mut name = name.to_string();
while name.len() < data_size {
name += " ";
}
let ipld = Node::branch(&name, links);
let bytes = DagCborCodec.encode(&ipld).unwrap();
let hash = Code::Sha2_256.digest(&bytes);
OwnedBlock::new(Cid::new_v1(0x71, hash), bytes)
}
fn fmt_cid(cid: Cid) -> String {
cid.to_string()[8..12].to_string()
}
fn fmt_cids(x: impl IntoIterator<Item = Cid>) -> String {
x.into_iter().map(fmt_cid).collect::<Vec<_>>().join(",")
}
fn build_tree_0(
prefix: &str,
branch: u64,
depth: u64,
blocks: &mut Vec<OwnedBlock>,
) -> anyhow::Result<Cid> {
let children = if depth == 0 {
Vec::new()
} else {
let mut children = Vec::new();
for i in 0..branch {
let cid = build_tree_0(&format!("{}-{}", prefix, i), branch, depth - 1, blocks)?;
children.push(cid);
}
children
};
let block = block(prefix, children);
let cid = *block.cid();
blocks.push(block);
Ok(cid)
}
fn build_tree(prefix: &str, branch: u64, depth: u64) -> anyhow::Result<(Cid, Vec<OwnedBlock>)> {
let mut tmp = Vec::new();
let res = build_tree_0(prefix, branch, depth, &mut tmp)?;
Ok((res, tmp))
}
fn build_chain(prefix: &str, n: usize) -> anyhow::Result<(Cid, Vec<OwnedBlock>)> {
assert!(n > 0);
let mut blocks = Vec::with_capacity(n);
let mk_node = |i: usize, links| block(&format!("{}-{}", prefix, i), links);
let mut prev: Option<Cid> = None;
for i in 0..n {
let node = mk_node(i, prev);
prev = Some(*node.cid());
blocks.push(node);
}
Ok((prev.unwrap(), blocks))
}
fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_span_events(FmtSpan::CLOSE)
.with_env_filter(EnvFilter::from_default_env())
.init();
let mut store = BlockStore::open("test.sqlite", Config::default())?;
for i in 0..10 {
println!("Adding filler tree {}", i);
let (tree_root, tree_blocks) = build_tree(&format!("tree-{}", i), 10, 4)?;
store.put_blocks(tree_blocks, None)?;
if i % 2 == 0 {
store.alias(&format!("tree-alias-{}", i).as_bytes(), Some(&tree_root))?;
}
}
let (tree_root, tree_blocks) = build_tree("test-tree", 10, 4)?;
let (list_root, list_blocks) = build_chain("chain", 10000)?;
store.put_blocks(tree_blocks, None)?;
store.put_blocks(list_blocks, None)?;
let t0 = Instant::now();
store.get_missing_blocks::<Vec<_>>(&list_root)?;
store.alias("list-alias-1".as_bytes(), Some(&list_root))?;
println!("get_missing_blocks {}", t0.elapsed().as_secs_f64());
println!(
"descendants of {:?} {}",
tree_root,
store.get_descendants::<Vec<_>>(&tree_root)?.len(),
);
store.put_block(&block("a", None), None)?;
println!(
"{:?}",
fmt_cids(store.get_missing_blocks::<Vec<_>>(block("a", None).cid())?)
);
store.put_block(&block("b", None), None)?;
store.put_block(&block("c", None), None)?;
println!(
"{:?}",
fmt_cids(store.get_descendants::<Vec<_>>(block("a", None).cid())?)
);
store.put_block(&block("d", None), None)?;
store.alias(b"source1", Some(block("a", None).cid()))?;
store.alias(b"source2", Some(block("d", None).cid()))?;
println!("starting gc");
let t0 = Instant::now();
store.gc()?;
let dt = Instant::now() - t0;
println!("{}", dt.as_secs_f64());
Ok(())
}