1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::{linked, loose};
use git_hash::ObjectId;
use std::{borrow::Borrow, option::Option::None, sync::Arc};

#[allow(clippy::large_enum_variant)]
enum DbState {
    Pack { pack_index: usize, entry_index: u32 },
    Loose { iter: loose::db::iter::Type },
}

impl Default for DbState {
    fn default() -> Self {
        DbState::Pack {
            pack_index: 0,
            entry_index: 0,
        }
    }
}

/// An iterator over all objects of a linked database
pub struct AllObjects<Db> {
    db: Db,
    db_index: usize,
    db_state: DbState,
}

impl<Db> AllObjects<Db>
where
    Db: Borrow<linked::Db>,
{
    /// Create a new iterator from a linked database
    pub fn new(db: Db) -> Self {
        let db_index = 0;
        let db_state = {
            let db = &db
                .borrow()
                .dbs
                .get(db_index)
                .expect("at least one db or no linked::Db at all");
            if db.packs.is_empty() {
                DbState::Loose { iter: db.loose.iter() }
            } else {
                DbState::default()
            }
        };
        AllObjects { db, db_index, db_state }
    }
}

impl<Db> Iterator for AllObjects<Db>
where
    Db: Borrow<linked::Db>,
{
    type Item = Result<ObjectId, loose::db::iter::Error>;

    fn next(&mut self) -> Option<Self::Item> {
        let db = self.db.borrow();
        if self.db_index == db.dbs.len() {
            return None;
        }

        match &mut self.db_state {
            DbState::Pack {
                pack_index,
                entry_index,
            } => {
                let db = &db.dbs[self.db_index];
                match db.packs.get(*pack_index) {
                    Some(bundle) => {
                        if *entry_index < bundle.index.num_objects() {
                            let oid = bundle.index.oid_at_index(*entry_index).to_owned();
                            *entry_index += 1;
                            Some(Ok(oid))
                        } else {
                            *pack_index += 1;
                            *entry_index = 0;
                            self.next()
                        }
                    }
                    None => {
                        self.db_state = DbState::Loose { iter: db.loose.iter() };
                        self.next()
                    }
                }
            }
            DbState::Loose { iter } => match iter.next() {
                Some(id) => Some(id),
                None => {
                    self.db_index += 1;
                    self.db_state = Default::default();
                    self.next()
                }
            },
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let packed_objects = self.db.borrow().dbs.iter().fold(0usize, |dbc, db| {
            dbc.saturating_add(
                db.packs
                    .iter()
                    .fold(0, |pc, pack| pc.saturating_add(pack.index.num_objects() as usize)),
            )
        });
        (packed_objects, None)
    }
}

impl linked::Db {
    /// Return an iterator over all objects in all linked databases, database after database, first packed
    /// objects with the 'best' packs first, followed by loose objects.
    /// For specialized iterations, use the `dbs` fields directly as all databases are accessible.
    pub fn iter(&self) -> AllObjects<&linked::Db> {
        AllObjects::new(self)
    }

    /// Like [`iter()`][linked::Db::iter()] but works with this instance living in an [`Arc`]
    ///
    /// Useful in conjunction with `'static threads`.
    pub fn arc_iter(self: &Arc<linked::Db>) -> AllObjects<Arc<linked::Db>> {
        AllObjects::new(Arc::clone(&self))
    }
}