mod search;
use std::cmp::Ordering;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use hashtree_core::{
Cid, DirEntry, HashTree, HashTreeConfig, HashTreeError, LinkType, Store, TreeEntry,
};
pub use search::{
SearchError, SearchIndex, SearchIndexOptions, SearchLinkResult, SearchOptions, SearchResult,
};
const DEFAULT_ORDER: usize = 32;
#[derive(Debug, Clone, Default)]
pub struct BTreeOptions {
pub order: Option<usize>,
}
#[derive(Debug, thiserror::Error)]
pub enum BTreeError {
#[error("hash tree error: {0}")]
HashTree(#[from] HashTreeError),
#[error("value was not valid utf-8: {0}")]
Utf8(#[from] std::string::FromUtf8Error),
}
#[derive(Debug, Clone)]
struct SplitResult {
left: Cid,
right: Cid,
left_first_key: String,
right_first_key: String,
left_count: u64,
right_count: u64,
}
#[derive(Debug, Clone)]
enum InsertValue {
String(String),
Link(Cid),
}
type BTreeFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, BTreeError>> + 'a>>;
pub struct BTree<S: Store> {
tree: HashTree<S>,
max_keys: usize,
}
#[derive(Debug, Clone)]
struct BuiltNode {
first_key: String,
cid: Cid,
count: u64,
}
impl<S: Store> BTree<S> {
pub fn new(store: Arc<S>, options: BTreeOptions) -> Self {
let order = options.order.unwrap_or(DEFAULT_ORDER).max(2);
Self {
tree: HashTree::new(HashTreeConfig::new(store)),
max_keys: order - 1,
}
}
pub async fn insert(
&self,
root: Option<&Cid>,
key: &str,
value: &str,
) -> Result<Cid, BTreeError> {
if let Some(root) = root {
if self.get(Some(root), key).await?.as_deref() == Some(value) {
return Ok(root.clone());
}
let result = self
.insert_recursive(
root.clone(),
key.to_string(),
InsertValue::String(value.to_string()),
)
.await?;
return self.finish_insert(result).await;
}
self.create_leaf(&[(key.to_string(), value.to_string())])
.await
}
pub async fn get(&self, root: Option<&Cid>, key: &str) -> Result<Option<String>, BTreeError> {
let Some(root) = root else {
return Ok(None);
};
self.get_recursive(root.clone(), key.to_string()).await
}
pub async fn insert_link(
&self,
root: Option<&Cid>,
key: &str,
target_cid: &Cid,
) -> Result<Cid, BTreeError> {
if let Some(root) = root {
if self
.get_link(Some(root), key)
.await?
.is_some_and(|existing| cid_equals(&existing, target_cid))
{
return Ok(root.clone());
}
let result = self
.insert_recursive(
root.clone(),
key.to_string(),
InsertValue::Link(target_cid.clone()),
)
.await?;
return self.finish_insert(result).await;
}
self.create_leaf_with_links(&[(key.to_string(), target_cid.clone())])
.await
}
pub async fn insert_link_unchecked(
&self,
root: Option<&Cid>,
key: &str,
target_cid: &Cid,
) -> Result<Cid, BTreeError> {
if let Some(root) = root {
let result = self
.insert_recursive(
root.clone(),
key.to_string(),
InsertValue::Link(target_cid.clone()),
)
.await?;
return self.finish_insert(result).await;
}
self.create_leaf_with_links(&[(key.to_string(), target_cid.clone())])
.await
}
pub async fn get_link(&self, root: Option<&Cid>, key: &str) -> Result<Option<Cid>, BTreeError> {
let Some(root) = root else {
return Ok(None);
};
self.get_link_recursive(root.clone(), key.to_string()).await
}
pub async fn entries(&self, root: Option<&Cid>) -> Result<Vec<(String, String)>, BTreeError> {
let Some(root) = root else {
return Ok(Vec::new());
};
self.traverse_in_order(root.clone()).await
}
pub async fn links_entries(
&self,
root: Option<&Cid>,
) -> Result<Vec<(String, Cid)>, BTreeError> {
let Some(root) = root else {
return Ok(Vec::new());
};
self.traverse_links_in_order(root.clone()).await
}
pub async fn links_entries_limited(
&self,
root: Option<&Cid>,
limit: usize,
) -> Result<Vec<(String, Cid)>, BTreeError> {
if limit == 0 {
return Ok(Vec::new());
}
let Some(root) = root else {
return Ok(Vec::new());
};
self.range_link_traverse_limited(root.clone(), None, None, limit)
.await
}
pub async fn count_links(&self, root: Option<&Cid>) -> Result<u64, BTreeError> {
self.scan_links(root).await
}
pub async fn scan_links(&self, root: Option<&Cid>) -> Result<u64, BTreeError> {
let Some(root) = root else {
return Ok(0);
};
self.count_links_recursive(root.clone()).await
}
pub async fn count_stored_links(&self, root: Option<&Cid>) -> Result<Option<u64>, BTreeError> {
let Some(root) = root else {
return Ok(Some(0));
};
let entries = self.tree.list_directory(root).await?;
if is_leaf_node(&entries) {
return Ok(Some(count_link_entries(&entries)));
}
let mut count = 0;
for entry in &entries {
let Some(child_count) = stored_link_subtree_count(entry) else {
return Ok(None);
};
count += child_count;
}
Ok(Some(count))
}
pub async fn range(
&self,
root: &Cid,
start: Option<&str>,
end: Option<&str>,
) -> Result<Vec<(String, String)>, BTreeError> {
self.range_traverse(
root.clone(),
start.map(ToOwned::to_owned),
end.map(ToOwned::to_owned),
)
.await
}
pub async fn prefix(
&self,
root: &Cid,
prefix: &str,
) -> Result<Vec<(String, String)>, BTreeError> {
let end = increment_prefix(prefix);
self.range(root, Some(prefix), end.as_deref()).await
}
pub async fn prefix_links(
&self,
root: &Cid,
prefix: &str,
) -> Result<Vec<(String, Cid)>, BTreeError> {
let end = increment_prefix(prefix);
self.range_link_traverse(root.clone(), Some(prefix.to_string()), end)
.await
}
pub async fn prefix_links_limited(
&self,
root: &Cid,
prefix: &str,
limit: usize,
) -> Result<Vec<(String, Cid)>, BTreeError> {
if limit == 0 {
return Ok(Vec::new());
}
let end = increment_prefix(prefix);
self.range_link_traverse_limited(root.clone(), Some(prefix.to_string()), end, limit)
.await
}
pub async fn delete(&self, root: &Cid, key: &str) -> Result<Option<Cid>, BTreeError> {
self.delete_recursive(root.clone(), key.to_string()).await
}
pub async fn merge(
&self,
base: Option<&Cid>,
other: Option<&Cid>,
prefer_other: bool,
) -> Result<Option<Cid>, BTreeError> {
let Some(other) = other else {
return Ok(base.cloned());
};
let Some(mut result) = base.cloned().or_else(|| Some(other.clone())) else {
return Ok(None);
};
if base.is_none() {
return Ok(Some(result));
}
for (key, value) in self.entries(Some(other)).await? {
let existing = self.get(Some(&result), &key).await?;
if existing.is_none() || prefer_other {
result = self.insert(Some(&result), &key, &value).await?;
}
}
Ok(Some(result))
}
pub async fn merge_links(
&self,
base: Option<&Cid>,
other: Option<&Cid>,
prefer_other: bool,
) -> Result<Option<Cid>, BTreeError> {
let Some(other) = other else {
return Ok(base.cloned());
};
let Some(mut result) = base.cloned().or_else(|| Some(other.clone())) else {
return Ok(None);
};
if base.is_none() {
return Ok(Some(result));
}
for (key, value) in self.links_entries(Some(other)).await? {
let existing = self.get_link(Some(&result), &key).await?;
if existing.is_none() || prefer_other {
result = self.insert_link(Some(&result), &key, &value).await?;
}
}
Ok(Some(result))
}
pub async fn build<I>(&self, items: I) -> Result<Option<Cid>, BTreeError>
where
I: IntoIterator<Item = (String, String)>,
{
let mut sorted: Vec<(String, String)> = items.into_iter().collect();
if sorted.is_empty() {
return Ok(None);
}
sorted.sort_by(|left, right| left.0.cmp(&right.0));
let mut deduped = Vec::with_capacity(sorted.len());
for (key, value) in sorted {
if let Some((last_key, last_value)) = deduped.last_mut() {
if *last_key == key {
*last_value = value;
continue;
}
}
deduped.push((key, value));
}
let mut level = Vec::with_capacity(deduped.len().div_ceil(self.max_keys));
for chunk in deduped.chunks(self.max_keys) {
let cid = self.create_leaf(chunk).await?;
level.push(BuiltNode {
first_key: chunk[0].0.clone(),
cid,
count: 0,
});
}
while level.len() > 1 {
let mut next_level = Vec::with_capacity(level.len().div_ceil(self.max_keys));
for chunk in level.chunks(self.max_keys) {
let cid = self.create_internal_node(chunk).await?;
next_level.push(BuiltNode {
first_key: chunk[0].first_key.clone(),
cid,
count: 0,
});
}
level = next_level;
}
Ok(level.pop().map(|node| node.cid))
}
pub async fn build_links<I>(&self, items: I) -> Result<Option<Cid>, BTreeError>
where
I: IntoIterator<Item = (String, Cid)>,
{
let mut sorted: Vec<(String, Cid)> = items.into_iter().collect();
if sorted.is_empty() {
return Ok(None);
}
sorted.sort_by(|left, right| left.0.cmp(&right.0));
let mut deduped = Vec::with_capacity(sorted.len());
for (key, cid) in sorted {
if let Some((last_key, last_cid)) = deduped.last_mut() {
if *last_key == key {
*last_cid = cid;
continue;
}
}
deduped.push((key, cid));
}
let mut level = Vec::with_capacity(deduped.len().div_ceil(self.max_keys));
for chunk in deduped.chunks(self.max_keys) {
let cid = self.create_leaf_with_links(chunk).await?;
level.push(BuiltNode {
first_key: chunk[0].0.clone(),
cid,
count: chunk.len() as u64,
});
}
while level.len() > 1 {
let mut next_level = Vec::with_capacity(level.len().div_ceil(self.max_keys));
for chunk in level.chunks(self.max_keys) {
let cid = self.create_internal_node(chunk).await?;
next_level.push(BuiltNode {
first_key: chunk[0].first_key.clone(),
cid,
count: chunk.iter().map(|child| child.count).sum(),
});
}
level = next_level;
}
Ok(level.pop().map(|node| node.cid))
}
async fn finish_insert(&self, result: InsertResult) -> Result<Cid, BTreeError> {
if let Some(split) = result.split {
return self
.create_internal_root(
&split.left_first_key,
&split.left,
split.left_count,
&split.right_first_key,
&split.right,
split.right_count,
)
.await;
}
Ok(result.cid)
}
fn get_recursive<'a>(&'a self, root: Cid, key: String) -> BTreeFuture<'a, Option<String>> {
Box::pin(async move {
let entries = self.tree.list_directory(&root).await?;
if is_leaf_node(&entries) {
let escaped = escape_key(&key);
let Some(entry) = entries.iter().find(|entry| entry.name == escaped) else {
return Ok(None);
};
if entry.link_type != LinkType::Blob {
return Ok(None);
}
let cid = entry_cid(entry);
let Some(data) = self.tree.get(&cid, None).await? else {
return Ok(None);
};
return Ok(Some(String::from_utf8(data)?));
}
let child = find_child(&entries, &key);
self.get_recursive(entry_cid(&child), key).await
})
}
fn get_link_recursive<'a>(&'a self, root: Cid, key: String) -> BTreeFuture<'a, Option<Cid>> {
Box::pin(async move {
let entries = self.tree.list_directory(&root).await?;
if is_leaf_node(&entries) {
let escaped = escape_key(&key);
let Some(entry) = entries.iter().find(|entry| entry.name == escaped) else {
return Ok(None);
};
if entry.link_type != LinkType::File {
return Ok(None);
}
return Ok(Some(entry_cid(entry)));
}
let child = find_child(&entries, &key);
self.get_link_recursive(entry_cid(&child), key).await
})
}
fn insert_recursive<'a>(
&'a self,
node: Cid,
key: String,
value: InsertValue,
) -> BTreeFuture<'a, InsertResult> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
if is_leaf_node(&entries) {
return self.insert_into_leaf(node, entries, key, value).await;
}
self.insert_into_internal(node, entries, key, value).await
})
}
fn insert_into_leaf<'a>(
&'a self,
node: Cid,
_entries: Vec<TreeEntry>,
key: String,
value: InsertValue,
) -> BTreeFuture<'a, InsertResult> {
Box::pin(async move {
let escaped_key = escape_key(&key);
let (entry_cid, size, link_type) = match value {
InsertValue::String(value) => {
let (cid, size) = self.tree.put_file(value.as_bytes()).await?;
(cid, size, LinkType::Blob)
}
InsertValue::Link(cid) => (cid, 0, LinkType::File),
};
let new_node = self
.tree
.set_entry(&node, &[], &escaped_key, &entry_cid, size, link_type)
.await?;
let new_entries = self.tree.list_directory(&new_node).await?;
if new_entries.len() > self.max_keys {
return Ok(InsertResult {
cid: new_node,
count: count_link_entries_or_subtrees(self, &new_entries).await?,
split: Some(self.split_leaf(new_entries).await?),
});
}
Ok(InsertResult {
cid: new_node,
count: count_link_entries_or_subtrees(self, &new_entries).await?,
split: None,
})
})
}
fn insert_into_internal<'a>(
&'a self,
node: Cid,
entries: Vec<TreeEntry>,
key: String,
value: InsertValue,
) -> BTreeFuture<'a, InsertResult> {
Box::pin(async move {
let child = find_child(&entries, &key);
let child_name = child.name.clone();
let child_cid = entry_cid(&child);
let result = self.insert_recursive(child_cid, key, value).await?;
let mut new_node = self
.tree
.set_entry(
&node,
&[],
&child_name,
&result.cid,
result.count,
LinkType::Dir,
)
.await?;
if let Some(split) = result.split {
new_node = self.tree.remove_entry(&new_node, &[], &child_name).await?;
new_node = self
.tree
.set_entry(
&new_node,
&[],
&escape_key(&split.left_first_key),
&split.left,
split.left_count,
LinkType::Dir,
)
.await?;
new_node = self
.tree
.set_entry(
&new_node,
&[],
&escape_key(&split.right_first_key),
&split.right,
split.right_count,
LinkType::Dir,
)
.await?;
}
let new_entries = self.tree.list_directory(&new_node).await?;
if new_entries.len() > self.max_keys {
return Ok(InsertResult {
cid: new_node,
count: count_link_entries_or_subtrees(self, &new_entries).await?,
split: Some(self.split_internal(new_entries).await?),
});
}
Ok(InsertResult {
cid: new_node,
count: count_link_entries_or_subtrees(self, &new_entries).await?,
split: None,
})
})
}
async fn split_leaf(&self, entries: Vec<TreeEntry>) -> Result<SplitResult, BTreeError> {
let sorted = sort_entries(entries);
let mid = sorted.len() / 2;
let left_entries = &sorted[..mid];
let right_entries = &sorted[mid..];
let left = self.create_node_from_entries(left_entries).await?;
let right = self.create_node_from_entries(right_entries).await?;
Ok(SplitResult {
left,
right,
left_first_key: unescape_key(&left_entries[0].name),
right_first_key: unescape_key(&right_entries[0].name),
left_count: count_link_entries(left_entries),
right_count: count_link_entries(right_entries),
})
}
async fn split_internal(&self, entries: Vec<TreeEntry>) -> Result<SplitResult, BTreeError> {
let sorted = sort_entries(entries);
let mid = sorted.len() / 2;
let left_entries = &sorted[..mid];
let right_entries = &sorted[mid..];
let left = self.create_node_from_entries(left_entries).await?;
let right = self.create_node_from_entries(right_entries).await?;
Ok(SplitResult {
left,
right,
left_first_key: unescape_key(&left_entries[0].name),
right_first_key: unescape_key(&right_entries[0].name),
left_count: count_link_entries_or_subtrees(self, left_entries).await?,
right_count: count_link_entries_or_subtrees(self, right_entries).await?,
})
}
async fn create_leaf(&self, items: &[(String, String)]) -> Result<Cid, BTreeError> {
let mut entries = Vec::with_capacity(items.len());
for (key, value) in items {
let (cid, size) = self.tree.put_file(value.as_bytes()).await?;
entries.push(
DirEntry::from_cid(escape_key(key), &cid)
.with_size(size)
.with_link_type(LinkType::Blob),
);
}
Ok(self.tree.put_directory(entries).await?)
}
async fn create_leaf_with_links(&self, items: &[(String, Cid)]) -> Result<Cid, BTreeError> {
let entries: Vec<DirEntry> = items
.iter()
.map(|(key, cid)| {
DirEntry::from_cid(escape_key(key), cid).with_link_type(LinkType::File)
})
.collect();
Ok(self.tree.put_directory(entries).await?)
}
async fn create_internal_node(&self, children: &[BuiltNode]) -> Result<Cid, BTreeError> {
let entries: Vec<DirEntry> = children
.iter()
.map(|child| {
DirEntry::from_cid(escape_key(&child.first_key), &child.cid)
.with_size(child.count)
.with_link_type(LinkType::Dir)
})
.collect();
Ok(self.tree.put_directory(entries).await?)
}
async fn create_internal_root(
&self,
left_key: &str,
left: &Cid,
left_count: u64,
right_key: &str,
right: &Cid,
right_count: u64,
) -> Result<Cid, BTreeError> {
let entries = vec![
DirEntry::from_cid(escape_key(left_key), left)
.with_size(left_count)
.with_link_type(LinkType::Dir),
DirEntry::from_cid(escape_key(right_key), right)
.with_size(right_count)
.with_link_type(LinkType::Dir),
];
Ok(self.tree.put_directory(entries).await?)
}
async fn create_node_from_entries(&self, entries: &[TreeEntry]) -> Result<Cid, BTreeError> {
let dir_entries = entries
.iter()
.cloned()
.map(tree_entry_to_dir_entry)
.collect::<Vec<_>>();
Ok(self.tree.put_directory(dir_entries).await?)
}
fn delete_recursive<'a>(&'a self, root: Cid, key: String) -> BTreeFuture<'a, Option<Cid>> {
Box::pin(async move {
let entries = self.tree.list_directory(&root).await?;
if is_leaf_node(&entries) {
let escaped = escape_key(&key);
if !entries.iter().any(|entry| entry.name == escaped) {
return Ok(Some(root));
}
let new_root = self.tree.remove_entry(&root, &[], &escaped).await?;
let new_entries = self.tree.list_directory(&new_root).await?;
if new_entries.is_empty() {
return Ok(None);
}
return Ok(Some(new_root));
}
let child = find_child(&entries, &key);
let child_name = child.name.clone();
let new_child = self.delete_recursive(entry_cid(&child), key).await?;
let Some(new_child) = new_child else {
let new_root = self.tree.remove_entry(&root, &[], &child_name).await?;
let new_entries = self.tree.list_directory(&new_root).await?;
if new_entries.is_empty() {
return Ok(None);
}
if new_entries.len() == 1 && new_entries[0].link_type == LinkType::Dir {
return Ok(Some(entry_cid(&new_entries[0])));
}
return Ok(Some(new_root));
};
if cid_equals(&new_child, &entry_cid(&child)) {
return Ok(Some(root));
}
let updated = self
.tree
.set_entry(
&root,
&[],
&child_name,
&new_child,
count_link_entries_or_subtrees(
self,
&self.tree.list_directory(&new_child).await?,
)
.await?,
LinkType::Dir,
)
.await?;
Ok(Some(updated))
})
}
fn traverse_in_order<'a>(&'a self, node: Cid) -> BTreeFuture<'a, Vec<(String, String)>> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
let sorted = sort_entries(entries);
let mut out = Vec::new();
if is_leaf_node(&sorted) {
for entry in sorted {
if entry.link_type != LinkType::Blob {
continue;
}
let cid = entry_cid(&entry);
if let Some(data) = self.tree.get(&cid, None).await? {
out.push((unescape_key(&entry.name), String::from_utf8(data)?));
}
}
return Ok(out);
}
for child in sorted {
out.extend(self.traverse_in_order(entry_cid(&child)).await?);
}
Ok(out)
})
}
fn traverse_links_in_order<'a>(&'a self, node: Cid) -> BTreeFuture<'a, Vec<(String, Cid)>> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
let sorted = sort_entries(entries);
let mut out = Vec::new();
if is_leaf_node(&sorted) {
for entry in sorted {
if entry.link_type == LinkType::File {
out.push((unescape_key(&entry.name), entry_cid(&entry)));
}
}
return Ok(out);
}
for child in sorted {
out.extend(self.traverse_links_in_order(entry_cid(&child)).await?);
}
Ok(out)
})
}
fn range_traverse<'a>(
&'a self,
node: Cid,
start: Option<String>,
end: Option<String>,
) -> BTreeFuture<'a, Vec<(String, String)>> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
let sorted = sort_entries(entries);
let mut out = Vec::new();
if is_leaf_node(&sorted) {
for entry in sorted {
if entry.link_type != LinkType::Blob {
continue;
}
let key = unescape_key(&entry.name);
if start.as_ref().is_some_and(|start| key < *start) {
continue;
}
if end.as_ref().is_some_and(|end| key >= *end) {
return Ok(out);
}
let cid = entry_cid(&entry);
if let Some(data) = self.tree.get(&cid, None).await? {
out.push((key, String::from_utf8(data)?));
}
}
return Ok(out);
}
for (index, child) in sorted.iter().enumerate() {
let child_min = unescape_key(&child.name);
let child_max = sorted.get(index + 1).map(|entry| unescape_key(&entry.name));
if start.as_ref().is_some_and(|start| {
child_max
.as_ref()
.is_some_and(|child_max| child_max <= start)
}) {
continue;
}
if end.as_ref().is_some_and(|end| child_min >= *end) {
return Ok(out);
}
out.extend(
self.range_traverse(entry_cid(child), start.clone(), end.clone())
.await?,
);
}
Ok(out)
})
}
fn range_link_traverse<'a>(
&'a self,
node: Cid,
start: Option<String>,
end: Option<String>,
) -> BTreeFuture<'a, Vec<(String, Cid)>> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
let sorted = sort_entries(entries);
let mut out = Vec::new();
if is_leaf_node(&sorted) {
for entry in sorted {
if entry.link_type != LinkType::File {
continue;
}
let key = unescape_key(&entry.name);
if start.as_ref().is_some_and(|start| key < *start) {
continue;
}
if end.as_ref().is_some_and(|end| key >= *end) {
return Ok(out);
}
out.push((key, entry_cid(&entry)));
}
return Ok(out);
}
for (index, child) in sorted.iter().enumerate() {
let child_min = unescape_key(&child.name);
let child_max = sorted.get(index + 1).map(|entry| unescape_key(&entry.name));
if start.as_ref().is_some_and(|start| {
child_max
.as_ref()
.is_some_and(|child_max| child_max <= start)
}) {
continue;
}
if end.as_ref().is_some_and(|end| child_min >= *end) {
return Ok(out);
}
out.extend(
self.range_link_traverse(entry_cid(child), start.clone(), end.clone())
.await?,
);
}
Ok(out)
})
}
fn range_link_traverse_limited<'a>(
&'a self,
node: Cid,
start: Option<String>,
end: Option<String>,
limit: usize,
) -> BTreeFuture<'a, Vec<(String, Cid)>> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
let sorted = sort_entries(entries);
let mut out = Vec::new();
if is_leaf_node(&sorted) {
for entry in sorted {
if entry.link_type != LinkType::File {
continue;
}
let key = unescape_key(&entry.name);
if start.as_ref().is_some_and(|start| key < *start) {
continue;
}
if end.as_ref().is_some_and(|end| key >= *end) {
return Ok(out);
}
out.push((key, entry_cid(&entry)));
if out.len() >= limit {
return Ok(out);
}
}
return Ok(out);
}
for (index, child) in sorted.iter().enumerate() {
let child_min = unescape_key(&child.name);
let child_max = sorted.get(index + 1).map(|entry| unescape_key(&entry.name));
if start.as_ref().is_some_and(|start| {
child_max
.as_ref()
.is_some_and(|child_max| child_max <= start)
}) {
continue;
}
if end.as_ref().is_some_and(|end| child_min >= *end) {
return Ok(out);
}
let remaining = limit.saturating_sub(out.len());
if remaining == 0 {
return Ok(out);
}
out.extend(
self.range_link_traverse_limited(
entry_cid(child),
start.clone(),
end.clone(),
remaining,
)
.await?,
);
if out.len() >= limit {
return Ok(out);
}
}
Ok(out)
})
}
fn count_links_recursive<'a>(&'a self, node: Cid) -> BTreeFuture<'a, u64> {
Box::pin(async move {
let entries = self.tree.list_directory(&node).await?;
count_link_entries_or_subtrees(self, &entries).await
})
}
}
#[derive(Debug, Clone)]
struct InsertResult {
cid: Cid,
count: u64,
split: Option<SplitResult>,
}
pub fn escape_key(key: &str) -> String {
key.replace('%', "%25")
.replace('/', "%2F")
.replace('\0', "%00")
}
pub fn unescape_key(name: &str) -> String {
name.replace("%2F", "/")
.replace("%2f", "/")
.replace("%00", "\0")
.replace("%25", "%")
}
fn increment_prefix(value: &str) -> Option<String> {
if value.is_empty() {
return Some(String::new());
}
let mut chars: Vec<char> = value.chars().collect();
let last = chars.pop()?;
let next = char::from_u32(last as u32 + 1)?;
chars.push(next);
Some(chars.into_iter().collect())
}
fn cid_equals(left: &Cid, right: &Cid) -> bool {
left.hash == right.hash && left.key == right.key
}
fn is_leaf_node(entries: &[TreeEntry]) -> bool {
entries.is_empty() || entries.iter().any(|entry| entry.link_type != LinkType::Dir)
}
fn sort_entries(mut entries: Vec<TreeEntry>) -> Vec<TreeEntry> {
entries.sort_by(|left, right| compare_unescaped_names(&left.name, &right.name));
entries
}
fn compare_unescaped_names(left: &str, right: &str) -> Ordering {
unescape_key(left).cmp(&unescape_key(right))
}
fn find_child(entries: &[TreeEntry], key: &str) -> TreeEntry {
let sorted = sort_entries(entries.to_vec());
for window in sorted.windows(2) {
let next_name = unescape_key(&window[1].name);
if key < next_name.as_str() {
return window[0].clone();
}
}
sorted
.last()
.cloned()
.expect("internal nodes must have children")
}
fn entry_cid(entry: &TreeEntry) -> Cid {
Cid {
hash: entry.hash,
key: entry.key,
}
}
fn tree_entry_to_dir_entry(entry: TreeEntry) -> DirEntry {
let mut out = DirEntry::from_cid(&entry.name, &entry_cid(&entry))
.with_size(entry.size)
.with_link_type(entry.link_type);
if let Some(meta) = entry.meta {
out = out.with_meta(meta);
}
out
}
fn count_link_entries(entries: &[TreeEntry]) -> u64 {
entries
.iter()
.filter(|entry| entry.link_type == LinkType::File)
.count() as u64
}
fn stored_link_subtree_count(entry: &TreeEntry) -> Option<u64> {
if entry.link_type != LinkType::Dir || entry.size == 0 {
return None;
}
Some(entry.size)
}
async fn count_link_entries_or_subtrees<S: Store>(
btree: &BTree<S>,
entries: &[TreeEntry],
) -> Result<u64, BTreeError> {
if is_leaf_node(entries) {
return Ok(count_link_entries(entries));
}
let mut count = 0;
for entry in entries {
count += match stored_link_subtree_count(entry) {
Some(child_count) => child_count,
None => btree.count_links_recursive(entry_cid(entry)).await?,
};
}
Ok(count)
}