Skip to main content

hashtree_index/
lib.rs

1//! Content-addressed B-tree indexes backed by hashtree.
2
3mod search;
4
5use std::cmp::Ordering;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9
10use hashtree_core::{
11    Cid, DirEntry, HashTree, HashTreeConfig, HashTreeError, LinkType, Store, TreeEntry,
12};
13pub use search::{
14    SearchError, SearchIndex, SearchIndexOptions, SearchLinkResult, SearchOptions, SearchResult,
15};
16
17const DEFAULT_ORDER: usize = 32;
18
19#[derive(Debug, Clone, Default)]
20pub struct BTreeOptions {
21    pub order: Option<usize>,
22}
23
24#[derive(Debug, thiserror::Error)]
25pub enum BTreeError {
26    #[error("hash tree error: {0}")]
27    HashTree(#[from] HashTreeError),
28    #[error("value was not valid utf-8: {0}")]
29    Utf8(#[from] std::string::FromUtf8Error),
30}
31
32#[derive(Debug, Clone)]
33struct SplitResult {
34    left: Cid,
35    right: Cid,
36    left_first_key: String,
37    right_first_key: String,
38}
39
40#[derive(Debug, Clone)]
41enum InsertValue {
42    String(String),
43    Link(Cid),
44}
45
46type BTreeFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, BTreeError>> + 'a>>;
47
48pub struct BTree<S: Store> {
49    tree: HashTree<S>,
50    max_keys: usize,
51}
52
53#[derive(Debug, Clone)]
54struct BuiltNode {
55    first_key: String,
56    cid: Cid,
57}
58
59impl<S: Store> BTree<S> {
60    pub fn new(store: Arc<S>, options: BTreeOptions) -> Self {
61        let order = options.order.unwrap_or(DEFAULT_ORDER).max(2);
62        Self {
63            tree: HashTree::new(HashTreeConfig::new(store)),
64            max_keys: order - 1,
65        }
66    }
67
68    pub async fn insert(
69        &self,
70        root: Option<&Cid>,
71        key: &str,
72        value: &str,
73    ) -> Result<Cid, BTreeError> {
74        if let Some(root) = root {
75            if self.get(Some(root), key).await?.as_deref() == Some(value) {
76                return Ok(root.clone());
77            }
78
79            let result = self
80                .insert_recursive(
81                    root.clone(),
82                    key.to_string(),
83                    InsertValue::String(value.to_string()),
84                )
85                .await?;
86            return self.finish_insert(result).await;
87        }
88
89        self.create_leaf(&[(key.to_string(), value.to_string())])
90            .await
91    }
92
93    pub async fn get(&self, root: Option<&Cid>, key: &str) -> Result<Option<String>, BTreeError> {
94        let Some(root) = root else {
95            return Ok(None);
96        };
97        self.get_recursive(root.clone(), key.to_string()).await
98    }
99
100    pub async fn insert_link(
101        &self,
102        root: Option<&Cid>,
103        key: &str,
104        target_cid: &Cid,
105    ) -> Result<Cid, BTreeError> {
106        if let Some(root) = root {
107            if self
108                .get_link(Some(root), key)
109                .await?
110                .is_some_and(|existing| cid_equals(&existing, target_cid))
111            {
112                return Ok(root.clone());
113            }
114
115            let result = self
116                .insert_recursive(
117                    root.clone(),
118                    key.to_string(),
119                    InsertValue::Link(target_cid.clone()),
120                )
121                .await?;
122            return self.finish_insert(result).await;
123        }
124
125        self.create_leaf_with_links(&[(key.to_string(), target_cid.clone())])
126            .await
127    }
128
129    pub async fn insert_link_unchecked(
130        &self,
131        root: Option<&Cid>,
132        key: &str,
133        target_cid: &Cid,
134    ) -> Result<Cid, BTreeError> {
135        if let Some(root) = root {
136            let result = self
137                .insert_recursive(
138                    root.clone(),
139                    key.to_string(),
140                    InsertValue::Link(target_cid.clone()),
141                )
142                .await?;
143            return self.finish_insert(result).await;
144        }
145
146        self.create_leaf_with_links(&[(key.to_string(), target_cid.clone())])
147            .await
148    }
149
150    pub async fn get_link(&self, root: Option<&Cid>, key: &str) -> Result<Option<Cid>, BTreeError> {
151        let Some(root) = root else {
152            return Ok(None);
153        };
154        self.get_link_recursive(root.clone(), key.to_string()).await
155    }
156
157    pub async fn entries(&self, root: Option<&Cid>) -> Result<Vec<(String, String)>, BTreeError> {
158        let Some(root) = root else {
159            return Ok(Vec::new());
160        };
161        self.traverse_in_order(root.clone()).await
162    }
163
164    pub async fn links_entries(
165        &self,
166        root: Option<&Cid>,
167    ) -> Result<Vec<(String, Cid)>, BTreeError> {
168        let Some(root) = root else {
169            return Ok(Vec::new());
170        };
171        self.traverse_links_in_order(root.clone()).await
172    }
173
174    pub async fn range(
175        &self,
176        root: &Cid,
177        start: Option<&str>,
178        end: Option<&str>,
179    ) -> Result<Vec<(String, String)>, BTreeError> {
180        self.range_traverse(
181            root.clone(),
182            start.map(ToOwned::to_owned),
183            end.map(ToOwned::to_owned),
184        )
185        .await
186    }
187
188    pub async fn prefix(
189        &self,
190        root: &Cid,
191        prefix: &str,
192    ) -> Result<Vec<(String, String)>, BTreeError> {
193        let end = increment_prefix(prefix);
194        self.range(root, Some(prefix), end.as_deref()).await
195    }
196
197    pub async fn prefix_links(
198        &self,
199        root: &Cid,
200        prefix: &str,
201    ) -> Result<Vec<(String, Cid)>, BTreeError> {
202        let end = increment_prefix(prefix);
203        self.range_link_traverse(root.clone(), Some(prefix.to_string()), end)
204            .await
205    }
206
207    pub async fn delete(&self, root: &Cid, key: &str) -> Result<Option<Cid>, BTreeError> {
208        self.delete_recursive(root.clone(), key.to_string()).await
209    }
210
211    pub async fn merge(
212        &self,
213        base: Option<&Cid>,
214        other: Option<&Cid>,
215        prefer_other: bool,
216    ) -> Result<Option<Cid>, BTreeError> {
217        let Some(other) = other else {
218            return Ok(base.cloned());
219        };
220        let Some(mut result) = base.cloned().or_else(|| Some(other.clone())) else {
221            return Ok(None);
222        };
223        if base.is_none() {
224            return Ok(Some(result));
225        }
226
227        for (key, value) in self.entries(Some(other)).await? {
228            let existing = self.get(Some(&result), &key).await?;
229            if existing.is_none() || prefer_other {
230                result = self.insert(Some(&result), &key, &value).await?;
231            }
232        }
233
234        Ok(Some(result))
235    }
236
237    pub async fn merge_links(
238        &self,
239        base: Option<&Cid>,
240        other: Option<&Cid>,
241        prefer_other: bool,
242    ) -> Result<Option<Cid>, BTreeError> {
243        let Some(other) = other else {
244            return Ok(base.cloned());
245        };
246        let Some(mut result) = base.cloned().or_else(|| Some(other.clone())) else {
247            return Ok(None);
248        };
249        if base.is_none() {
250            return Ok(Some(result));
251        }
252
253        for (key, value) in self.links_entries(Some(other)).await? {
254            let existing = self.get_link(Some(&result), &key).await?;
255            if existing.is_none() || prefer_other {
256                result = self.insert_link(Some(&result), &key, &value).await?;
257            }
258        }
259
260        Ok(Some(result))
261    }
262
263    pub async fn build<I>(&self, items: I) -> Result<Option<Cid>, BTreeError>
264    where
265        I: IntoIterator<Item = (String, String)>,
266    {
267        let mut sorted: Vec<(String, String)> = items.into_iter().collect();
268        if sorted.is_empty() {
269            return Ok(None);
270        }
271
272        sorted.sort_by(|left, right| left.0.cmp(&right.0));
273
274        let mut deduped = Vec::with_capacity(sorted.len());
275        for (key, value) in sorted {
276            if let Some((last_key, last_value)) = deduped.last_mut() {
277                if *last_key == key {
278                    *last_value = value;
279                    continue;
280                }
281            }
282            deduped.push((key, value));
283        }
284
285        let mut level = Vec::with_capacity(deduped.len().div_ceil(self.max_keys));
286        for chunk in deduped.chunks(self.max_keys) {
287            let cid = self.create_leaf(chunk).await?;
288            level.push(BuiltNode {
289                first_key: chunk[0].0.clone(),
290                cid,
291            });
292        }
293
294        while level.len() > 1 {
295            let mut next_level = Vec::with_capacity(level.len().div_ceil(self.max_keys));
296            for chunk in level.chunks(self.max_keys) {
297                let cid = self.create_internal_node(chunk).await?;
298                next_level.push(BuiltNode {
299                    first_key: chunk[0].first_key.clone(),
300                    cid,
301                });
302            }
303            level = next_level;
304        }
305
306        Ok(level.pop().map(|node| node.cid))
307    }
308
309    pub async fn build_links<I>(&self, items: I) -> Result<Option<Cid>, BTreeError>
310    where
311        I: IntoIterator<Item = (String, Cid)>,
312    {
313        let mut sorted: Vec<(String, Cid)> = items.into_iter().collect();
314        if sorted.is_empty() {
315            return Ok(None);
316        }
317
318        sorted.sort_by(|left, right| left.0.cmp(&right.0));
319
320        let mut deduped = Vec::with_capacity(sorted.len());
321        for (key, cid) in sorted {
322            if let Some((last_key, last_cid)) = deduped.last_mut() {
323                if *last_key == key {
324                    *last_cid = cid;
325                    continue;
326                }
327            }
328            deduped.push((key, cid));
329        }
330
331        let mut level = Vec::with_capacity(deduped.len().div_ceil(self.max_keys));
332        for chunk in deduped.chunks(self.max_keys) {
333            let cid = self.create_leaf_with_links(chunk).await?;
334            level.push(BuiltNode {
335                first_key: chunk[0].0.clone(),
336                cid,
337            });
338        }
339
340        while level.len() > 1 {
341            let mut next_level = Vec::with_capacity(level.len().div_ceil(self.max_keys));
342            for chunk in level.chunks(self.max_keys) {
343                let cid = self.create_internal_node(chunk).await?;
344                next_level.push(BuiltNode {
345                    first_key: chunk[0].first_key.clone(),
346                    cid,
347                });
348            }
349            level = next_level;
350        }
351
352        Ok(level.pop().map(|node| node.cid))
353    }
354
355    async fn finish_insert(&self, result: InsertResult) -> Result<Cid, BTreeError> {
356        if let Some(split) = result.split {
357            return self
358                .create_internal_root(
359                    &split.left_first_key,
360                    &split.left,
361                    &split.right_first_key,
362                    &split.right,
363                )
364                .await;
365        }
366        Ok(result.cid)
367    }
368
369    fn get_recursive<'a>(&'a self, root: Cid, key: String) -> BTreeFuture<'a, Option<String>> {
370        Box::pin(async move {
371            let entries = self.tree.list_directory(&root).await?;
372            if is_leaf_node(&entries) {
373                let escaped = escape_key(&key);
374                let Some(entry) = entries.iter().find(|entry| entry.name == escaped) else {
375                    return Ok(None);
376                };
377                if entry.link_type != LinkType::Blob {
378                    return Ok(None);
379                }
380
381                let cid = entry_cid(entry);
382                let Some(data) = self.tree.get(&cid, None).await? else {
383                    return Ok(None);
384                };
385                return Ok(Some(String::from_utf8(data)?));
386            }
387
388            let child = find_child(&entries, &key);
389            self.get_recursive(entry_cid(&child), key).await
390        })
391    }
392
393    fn get_link_recursive<'a>(&'a self, root: Cid, key: String) -> BTreeFuture<'a, Option<Cid>> {
394        Box::pin(async move {
395            let entries = self.tree.list_directory(&root).await?;
396            if is_leaf_node(&entries) {
397                let escaped = escape_key(&key);
398                let Some(entry) = entries.iter().find(|entry| entry.name == escaped) else {
399                    return Ok(None);
400                };
401                if entry.link_type != LinkType::File {
402                    return Ok(None);
403                }
404                return Ok(Some(entry_cid(entry)));
405            }
406
407            let child = find_child(&entries, &key);
408            self.get_link_recursive(entry_cid(&child), key).await
409        })
410    }
411
412    fn insert_recursive<'a>(
413        &'a self,
414        node: Cid,
415        key: String,
416        value: InsertValue,
417    ) -> BTreeFuture<'a, InsertResult> {
418        Box::pin(async move {
419            let entries = self.tree.list_directory(&node).await?;
420            if is_leaf_node(&entries) {
421                return self.insert_into_leaf(node, entries, key, value).await;
422            }
423            self.insert_into_internal(node, entries, key, value).await
424        })
425    }
426
427    fn insert_into_leaf<'a>(
428        &'a self,
429        node: Cid,
430        _entries: Vec<TreeEntry>,
431        key: String,
432        value: InsertValue,
433    ) -> BTreeFuture<'a, InsertResult> {
434        Box::pin(async move {
435            let escaped_key = escape_key(&key);
436            let (entry_cid, size, link_type) = match value {
437                InsertValue::String(value) => {
438                    let (cid, size) = self.tree.put_file(value.as_bytes()).await?;
439                    (cid, size, LinkType::Blob)
440                }
441                InsertValue::Link(cid) => (cid, 0, LinkType::File),
442            };
443
444            let new_node = self
445                .tree
446                .set_entry(&node, &[], &escaped_key, &entry_cid, size, link_type)
447                .await?;
448
449            let new_entries = self.tree.list_directory(&new_node).await?;
450            if new_entries.len() > self.max_keys {
451                return Ok(InsertResult {
452                    cid: new_node,
453                    split: Some(self.split_leaf(new_entries).await?),
454                });
455            }
456
457            Ok(InsertResult {
458                cid: new_node,
459                split: None,
460            })
461        })
462    }
463
464    fn insert_into_internal<'a>(
465        &'a self,
466        node: Cid,
467        entries: Vec<TreeEntry>,
468        key: String,
469        value: InsertValue,
470    ) -> BTreeFuture<'a, InsertResult> {
471        Box::pin(async move {
472            let child = find_child(&entries, &key);
473            let child_name = child.name.clone();
474            let child_cid = entry_cid(&child);
475            let result = self.insert_recursive(child_cid, key, value).await?;
476
477            let mut new_node = self
478                .tree
479                .set_entry(&node, &[], &child_name, &result.cid, 0, LinkType::Dir)
480                .await?;
481
482            if let Some(split) = result.split {
483                new_node = self.tree.remove_entry(&new_node, &[], &child_name).await?;
484                new_node = self
485                    .tree
486                    .set_entry(
487                        &new_node,
488                        &[],
489                        &escape_key(&split.left_first_key),
490                        &split.left,
491                        0,
492                        LinkType::Dir,
493                    )
494                    .await?;
495                new_node = self
496                    .tree
497                    .set_entry(
498                        &new_node,
499                        &[],
500                        &escape_key(&split.right_first_key),
501                        &split.right,
502                        0,
503                        LinkType::Dir,
504                    )
505                    .await?;
506            }
507
508            let new_entries = self.tree.list_directory(&new_node).await?;
509            if new_entries.len() > self.max_keys {
510                return Ok(InsertResult {
511                    cid: new_node,
512                    split: Some(self.split_internal(new_entries).await?),
513                });
514            }
515
516            Ok(InsertResult {
517                cid: new_node,
518                split: None,
519            })
520        })
521    }
522
523    async fn split_leaf(&self, entries: Vec<TreeEntry>) -> Result<SplitResult, BTreeError> {
524        let sorted = sort_entries(entries);
525        let mid = sorted.len() / 2;
526        let left_entries = &sorted[..mid];
527        let right_entries = &sorted[mid..];
528
529        let left = self.create_node_from_entries(left_entries).await?;
530        let right = self.create_node_from_entries(right_entries).await?;
531
532        Ok(SplitResult {
533            left,
534            right,
535            left_first_key: unescape_key(&left_entries[0].name),
536            right_first_key: unescape_key(&right_entries[0].name),
537        })
538    }
539
540    async fn split_internal(&self, entries: Vec<TreeEntry>) -> Result<SplitResult, BTreeError> {
541        let sorted = sort_entries(entries);
542        let mid = sorted.len() / 2;
543        let left_entries = &sorted[..mid];
544        let right_entries = &sorted[mid..];
545
546        let left = self.create_node_from_entries(left_entries).await?;
547        let right = self.create_node_from_entries(right_entries).await?;
548
549        Ok(SplitResult {
550            left,
551            right,
552            left_first_key: unescape_key(&left_entries[0].name),
553            right_first_key: unescape_key(&right_entries[0].name),
554        })
555    }
556
557    async fn create_leaf(&self, items: &[(String, String)]) -> Result<Cid, BTreeError> {
558        let mut entries = Vec::with_capacity(items.len());
559        for (key, value) in items {
560            let (cid, size) = self.tree.put_file(value.as_bytes()).await?;
561            entries.push(
562                DirEntry::from_cid(escape_key(key), &cid)
563                    .with_size(size)
564                    .with_link_type(LinkType::Blob),
565            );
566        }
567        Ok(self.tree.put_directory(entries).await?)
568    }
569
570    async fn create_leaf_with_links(&self, items: &[(String, Cid)]) -> Result<Cid, BTreeError> {
571        let entries: Vec<DirEntry> = items
572            .iter()
573            .map(|(key, cid)| {
574                DirEntry::from_cid(escape_key(key), cid).with_link_type(LinkType::File)
575            })
576            .collect();
577        Ok(self.tree.put_directory(entries).await?)
578    }
579
580    async fn create_internal_node(&self, children: &[BuiltNode]) -> Result<Cid, BTreeError> {
581        let entries: Vec<DirEntry> = children
582            .iter()
583            .map(|child| {
584                DirEntry::from_cid(escape_key(&child.first_key), &child.cid)
585                    .with_link_type(LinkType::Dir)
586            })
587            .collect();
588        Ok(self.tree.put_directory(entries).await?)
589    }
590
591    async fn create_internal_root(
592        &self,
593        left_key: &str,
594        left: &Cid,
595        right_key: &str,
596        right: &Cid,
597    ) -> Result<Cid, BTreeError> {
598        let entries = vec![
599            DirEntry::from_cid(escape_key(left_key), left).with_link_type(LinkType::Dir),
600            DirEntry::from_cid(escape_key(right_key), right).with_link_type(LinkType::Dir),
601        ];
602        Ok(self.tree.put_directory(entries).await?)
603    }
604
605    async fn create_node_from_entries(&self, entries: &[TreeEntry]) -> Result<Cid, BTreeError> {
606        let dir_entries = entries
607            .iter()
608            .cloned()
609            .map(tree_entry_to_dir_entry)
610            .collect::<Vec<_>>();
611        Ok(self.tree.put_directory(dir_entries).await?)
612    }
613
614    fn delete_recursive<'a>(&'a self, root: Cid, key: String) -> BTreeFuture<'a, Option<Cid>> {
615        Box::pin(async move {
616            let entries = self.tree.list_directory(&root).await?;
617            if is_leaf_node(&entries) {
618                let escaped = escape_key(&key);
619                if !entries.iter().any(|entry| entry.name == escaped) {
620                    return Ok(Some(root));
621                }
622
623                let new_root = self.tree.remove_entry(&root, &[], &escaped).await?;
624                let new_entries = self.tree.list_directory(&new_root).await?;
625                if new_entries.is_empty() {
626                    return Ok(None);
627                }
628                return Ok(Some(new_root));
629            }
630
631            let child = find_child(&entries, &key);
632            let child_name = child.name.clone();
633            let new_child = self.delete_recursive(entry_cid(&child), key).await?;
634
635            let Some(new_child) = new_child else {
636                let new_root = self.tree.remove_entry(&root, &[], &child_name).await?;
637                let new_entries = self.tree.list_directory(&new_root).await?;
638                if new_entries.is_empty() {
639                    return Ok(None);
640                }
641                if new_entries.len() == 1 && new_entries[0].link_type == LinkType::Dir {
642                    return Ok(Some(entry_cid(&new_entries[0])));
643                }
644                return Ok(Some(new_root));
645            };
646
647            if cid_equals(&new_child, &entry_cid(&child)) {
648                return Ok(Some(root));
649            }
650
651            let updated = self
652                .tree
653                .set_entry(&root, &[], &child_name, &new_child, 0, LinkType::Dir)
654                .await?;
655            Ok(Some(updated))
656        })
657    }
658
659    fn traverse_in_order<'a>(&'a self, node: Cid) -> BTreeFuture<'a, Vec<(String, String)>> {
660        Box::pin(async move {
661            let entries = self.tree.list_directory(&node).await?;
662            let sorted = sort_entries(entries);
663            let mut out = Vec::new();
664
665            if is_leaf_node(&sorted) {
666                for entry in sorted {
667                    if entry.link_type != LinkType::Blob {
668                        continue;
669                    }
670                    let cid = entry_cid(&entry);
671                    if let Some(data) = self.tree.get(&cid, None).await? {
672                        out.push((unescape_key(&entry.name), String::from_utf8(data)?));
673                    }
674                }
675                return Ok(out);
676            }
677
678            for child in sorted {
679                out.extend(self.traverse_in_order(entry_cid(&child)).await?);
680            }
681            Ok(out)
682        })
683    }
684
685    fn traverse_links_in_order<'a>(&'a self, node: Cid) -> BTreeFuture<'a, Vec<(String, Cid)>> {
686        Box::pin(async move {
687            let entries = self.tree.list_directory(&node).await?;
688            let sorted = sort_entries(entries);
689            let mut out = Vec::new();
690
691            if is_leaf_node(&sorted) {
692                for entry in sorted {
693                    if entry.link_type == LinkType::File {
694                        out.push((unescape_key(&entry.name), entry_cid(&entry)));
695                    }
696                }
697                return Ok(out);
698            }
699
700            for child in sorted {
701                out.extend(self.traverse_links_in_order(entry_cid(&child)).await?);
702            }
703            Ok(out)
704        })
705    }
706
707    fn range_traverse<'a>(
708        &'a self,
709        node: Cid,
710        start: Option<String>,
711        end: Option<String>,
712    ) -> BTreeFuture<'a, Vec<(String, String)>> {
713        Box::pin(async move {
714            let entries = self.tree.list_directory(&node).await?;
715            let sorted = sort_entries(entries);
716            let mut out = Vec::new();
717
718            if is_leaf_node(&sorted) {
719                for entry in sorted {
720                    if entry.link_type != LinkType::Blob {
721                        continue;
722                    }
723                    let key = unescape_key(&entry.name);
724                    if start.as_ref().is_some_and(|start| key < *start) {
725                        continue;
726                    }
727                    if end.as_ref().is_some_and(|end| key >= *end) {
728                        return Ok(out);
729                    }
730
731                    let cid = entry_cid(&entry);
732                    if let Some(data) = self.tree.get(&cid, None).await? {
733                        out.push((key, String::from_utf8(data)?));
734                    }
735                }
736                return Ok(out);
737            }
738
739            for (index, child) in sorted.iter().enumerate() {
740                let child_min = unescape_key(&child.name);
741                let child_max = sorted.get(index + 1).map(|entry| unescape_key(&entry.name));
742
743                if start.as_ref().is_some_and(|start| {
744                    child_max
745                        .as_ref()
746                        .is_some_and(|child_max| child_max <= start)
747                }) {
748                    continue;
749                }
750                if end.as_ref().is_some_and(|end| child_min >= *end) {
751                    return Ok(out);
752                }
753
754                out.extend(
755                    self.range_traverse(entry_cid(child), start.clone(), end.clone())
756                        .await?,
757                );
758            }
759
760            Ok(out)
761        })
762    }
763
764    fn range_link_traverse<'a>(
765        &'a self,
766        node: Cid,
767        start: Option<String>,
768        end: Option<String>,
769    ) -> BTreeFuture<'a, Vec<(String, Cid)>> {
770        Box::pin(async move {
771            let entries = self.tree.list_directory(&node).await?;
772            let sorted = sort_entries(entries);
773            let mut out = Vec::new();
774
775            if is_leaf_node(&sorted) {
776                for entry in sorted {
777                    if entry.link_type != LinkType::File {
778                        continue;
779                    }
780                    let key = unescape_key(&entry.name);
781                    if start.as_ref().is_some_and(|start| key < *start) {
782                        continue;
783                    }
784                    if end.as_ref().is_some_and(|end| key >= *end) {
785                        return Ok(out);
786                    }
787                    out.push((key, entry_cid(&entry)));
788                }
789                return Ok(out);
790            }
791
792            for (index, child) in sorted.iter().enumerate() {
793                let child_min = unescape_key(&child.name);
794                let child_max = sorted.get(index + 1).map(|entry| unescape_key(&entry.name));
795
796                if start.as_ref().is_some_and(|start| {
797                    child_max
798                        .as_ref()
799                        .is_some_and(|child_max| child_max <= start)
800                }) {
801                    continue;
802                }
803                if end.as_ref().is_some_and(|end| child_min >= *end) {
804                    return Ok(out);
805                }
806
807                out.extend(
808                    self.range_link_traverse(entry_cid(child), start.clone(), end.clone())
809                        .await?,
810                );
811            }
812
813            Ok(out)
814        })
815    }
816}
817
818#[derive(Debug, Clone)]
819struct InsertResult {
820    cid: Cid,
821    split: Option<SplitResult>,
822}
823
824pub fn escape_key(key: &str) -> String {
825    key.replace('%', "%25")
826        .replace('/', "%2F")
827        .replace('\0', "%00")
828}
829
830pub fn unescape_key(name: &str) -> String {
831    name.replace("%2F", "/")
832        .replace("%2f", "/")
833        .replace("%00", "\0")
834        .replace("%25", "%")
835}
836
837fn increment_prefix(value: &str) -> Option<String> {
838    if value.is_empty() {
839        return Some(String::new());
840    }
841
842    let mut chars: Vec<char> = value.chars().collect();
843    let last = chars.pop()?;
844    let next = char::from_u32(last as u32 + 1)?;
845    chars.push(next);
846    Some(chars.into_iter().collect())
847}
848
849fn cid_equals(left: &Cid, right: &Cid) -> bool {
850    left.hash == right.hash && left.key == right.key
851}
852
853fn is_leaf_node(entries: &[TreeEntry]) -> bool {
854    entries.is_empty() || entries.iter().any(|entry| entry.link_type != LinkType::Dir)
855}
856
857fn sort_entries(mut entries: Vec<TreeEntry>) -> Vec<TreeEntry> {
858    entries.sort_by(|left, right| compare_unescaped_names(&left.name, &right.name));
859    entries
860}
861
862fn compare_unescaped_names(left: &str, right: &str) -> Ordering {
863    unescape_key(left).cmp(&unescape_key(right))
864}
865
866fn find_child(entries: &[TreeEntry], key: &str) -> TreeEntry {
867    let sorted = sort_entries(entries.to_vec());
868    for window in sorted.windows(2) {
869        let next_name = unescape_key(&window[1].name);
870        if key < next_name.as_str() {
871            return window[0].clone();
872        }
873    }
874    sorted
875        .last()
876        .cloned()
877        .expect("internal nodes must have children")
878}
879
880fn entry_cid(entry: &TreeEntry) -> Cid {
881    Cid {
882        hash: entry.hash,
883        key: entry.key,
884    }
885}
886
887fn tree_entry_to_dir_entry(entry: TreeEntry) -> DirEntry {
888    let mut out = DirEntry::from_cid(&entry.name, &entry_cid(&entry))
889        .with_size(entry.size)
890        .with_link_type(entry.link_type);
891    if let Some(meta) = entry.meta {
892        out = out.with_meta(meta);
893    }
894    out
895}