-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support unixfs dag in mater #728
Changes from all commits
636873a
50300d4
5ba804f
9f00f3a
a40d50e
aaf146d
5e8e2d4
7d8e90a
397197d
3208be7
bcfb522
d73d259
68fa0eb
29c7354
7ef51e5
9a4485e
eb8fdfa
6a043bd
cc906f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -18,16 +18,138 @@ mod unixfs; | |||||||||||||||||||||||||||||||||||||||||||||||||
mod v1; | ||||||||||||||||||||||||||||||||||||||||||||||||||
mod v2; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// We need to re-expose this because `read_block` returns `(Cid, Vec<u8>)`. | ||||||||||||||||||||||||||||||||||||||||||||||||||
use std::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
collections::{HashMap, HashSet}, | ||||||||||||||||||||||||||||||||||||||||||||||||||
io::SeekFrom, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub use ipld_core::cid::Cid; | ||||||||||||||||||||||||||||||||||||||||||||||||||
use ipld_core::codec::Codec; | ||||||||||||||||||||||||||||||||||||||||||||||||||
use ipld_dagpb::DagPbCodec; | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub use multicodec::{DAG_PB_CODE, IDENTITY_CODE, RAW_CODE}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub use stores::{create_filestore, Blockstore, Config, FileBlockstore}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub use stores::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
create_filestore, Blockstore, Config, FileBlockstore, DEFAULT_CHUNK_SIZE, DEFAULT_TREE_WIDTH, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
use tokio::io::{AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWriteExt}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub use v1::{Header as CarV1Header, Reader as CarV1Reader, Writer as CarV1Writer}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub use v2::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
verify_cid, Characteristics, Header as CarV2Header, Index, IndexEntry, IndexSorted, | ||||||||||||||||||||||||||||||||||||||||||||||||||
MultihashIndexSorted, Reader as CarV2Reader, SingleWidthIndex, Writer as CarV2Writer, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Represents the location and size of a block in the CAR file. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub struct BlockLocation { | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// The byte offset in the CAR file where the block starts. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub offset: u64, | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// The size (in bytes) of the block. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub size: u64, | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// A simple blockstore backed by a CAR file and its index. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub struct CarBlockStore<R> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
reader: R, | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Mapping from CID to block location. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub index: HashMap<Cid, BlockLocation>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
impl<R> CarBlockStore<R> | ||||||||||||||||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||||||||||||||||
R: AsyncSeekExt + AsyncReadExt + Unpin, | ||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Extract content by traversing the UnixFS DAG using the index. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub async fn extract_content_via_index<W>( | ||||||||||||||||||||||||||||||||||||||||||||||||||
&mut self, | ||||||||||||||||||||||||||||||||||||||||||||||||||
root: &Cid, | ||||||||||||||||||||||||||||||||||||||||||||||||||
output: &mut W, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) -> Result<(), Error> | ||||||||||||||||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||||||||||||||||
W: AsyncWriteExt + Unpin, | ||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
// To avoid processing a block more than once. | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut processed = HashSet::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
// We use a stack for DFS traversal. | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut to_process = vec![*root]; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
while let Some(current_cid) = to_process.pop() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
if processed.contains(¤t_cid) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+74
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic is duplicate in the DFS. We're checking it already here.
|
||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
processed.insert(current_cid); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Retrieve block by CID via the index. | ||||||||||||||||||||||||||||||||||||||||||||||||||
let block_bytes = self.get_block(¤t_cid).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Write the raw block data. In a real UnixFS traversal you might need | ||||||||||||||||||||||||||||||||||||||||||||||||||
// to reconstruct file content in order. | ||||||||||||||||||||||||||||||||||||||||||||||||||
output.write_all(&block_bytes).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+82
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is that not real UnixFS traversal? |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// If the block is a DAG-PB node, decode and enqueue its children. | ||||||||||||||||||||||||||||||||||||||||||||||||||
if current_cid.codec() == crate::multicodec::DAG_PB_CODE { | ||||||||||||||||||||||||||||||||||||||||||||||||||
pete-eiger marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let mut cursor = std::io::Cursor::new(&block_bytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Propagate any error that occurs during decoding. | ||||||||||||||||||||||||||||||||||||||||||||||||||
let pb_node: ipld_dagpb::PbNode = | ||||||||||||||||||||||||||||||||||||||||||||||||||
DagPbCodec::decode(&mut cursor).map_err(Error::DagPbError)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
for link in pb_node.links { | ||||||||||||||||||||||||||||||||||||||||||||||||||
if !processed.contains(&link.cid) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
to_process.push(link.cid); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+87
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Ok(()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
impl<R> CarBlockStore<R> | ||||||||||||||||||||||||||||||||||||||||||||||||||
where | ||||||||||||||||||||||||||||||||||||||||||||||||||
R: AsyncSeek + AsyncReadExt + Unpin, | ||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+104
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we pull it in under one I don't know why one is |
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Given a reader positioned at the start of a CAR file, | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// load the CARv2 index and build a mapping of CID -> (offset, size). | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// For simplicity, assume the CAR header has been read and the index offset is known. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub async fn load_index( | ||||||||||||||||||||||||||||||||||||||||||||||||||
mut reader: R, | ||||||||||||||||||||||||||||||||||||||||||||||||||
index_offset: u64, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) -> Result<HashMap<Cid, BlockLocation>, Error> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Seek to the start of the index. | ||||||||||||||||||||||||||||||||||||||||||||||||||
reader.seek(SeekFrom::Start(index_offset)).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Parse the index according to the CARv2 spec. For demonstration, | ||||||||||||||||||||||||||||||||||||||||||||||||||
// we assume a very simple format where each index entry is: | ||||||||||||||||||||||||||||||||||||||||||||||||||
// [CID length (u8)][CID bytes][offset (u64)][size (u64)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut index = HashMap::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
// In a real implementation you’d read until EOF or index length. | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Here we use a simple loop: | ||||||||||||||||||||||||||||||||||||||||||||||||||
loop { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let cid_len = match reader.read_u8().await { | ||||||||||||||||||||||||||||||||||||||||||||||||||
Ok(n) => n as usize, | ||||||||||||||||||||||||||||||||||||||||||||||||||
Err(_) => break, | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut cid_buf = vec![0u8; cid_len]; | ||||||||||||||||||||||||||||||||||||||||||||||||||
reader.read_exact(&mut cid_buf).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let cid = Cid::try_from(cid_buf).map_err(|e| Error::Other(e.to_string()))?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let offset = reader.read_u64_le().await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let size = reader.read_u64_le().await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
index.insert(cid, BlockLocation { offset, size }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
Ok(index) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Retrieve a block by its CID. This method uses the in-memory index | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// to seek directly to the block’s location. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub async fn get_block(&mut self, cid: &Cid) -> Result<Vec<u8>, Error> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
if let Some(location) = self.index.get(cid) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.reader.seek(SeekFrom::Start(location.offset)).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut buf = vec![0u8; location.size as usize]; | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.reader.read_exact(&mut buf).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
Ok(buf) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||
Err(Error::BlockNotFound(cid.to_string())) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// CAR handling errors. | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[derive(Debug, thiserror::Error)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub enum Error { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -111,6 +233,14 @@ pub enum Error { | |||||||||||||||||||||||||||||||||||||||||||||||||
/// See [`DagPbError`](ipld_dagpb::Error) for more information. | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[error(transparent)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
DagPbError(#[from] ipld_dagpb::Error), | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Catch-all error for miscellaneous cases. | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[error("other error: {0}")] | ||||||||||||||||||||||||||||||||||||||||||||||||||
Other(String), | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Error indicating that the requested block could not be found found in the CAR file's index. | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[error("block not found: {0}")] | ||||||||||||||||||||||||||||||||||||||||||||||||||
BlockNotFound(String), | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We seem to not overwrite by default now. If we do not want to support overwriting can we remove this boolean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just not remove it the overwrite flag at all, what isn't broken doesn't need fixing :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are supporting (see #728 (comment))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overwrite flag was useful in our expriments, I'd let it be.