Skip to main content

yog_data/model/
map.rs

1//! A map of Yog Data: entries in file order, keys unique. A small map is
2//! searched by a scan, the fastest there is for few keys. Past
3//! [`INDEXED_FROM`] keys, a lookup builds an index of them once, and every
4//! later change keeps it current. Decoding never builds one, so a document
5//! read and never searched pays nothing for it.
6
7use std::collections::HashMap;
8use std::sync::OnceLock;
9
10use yog_sync::Arc;
11
12use super::value::Value;
13
14/// How many keys a map has before a lookup indexes them: below it a scan
15/// beats hashing (`benches/data.rs`'s maps, `yog-storage`'s records).
16const INDEXED_FROM: usize = 32;
17
18/// Keys are shared: a decoded document allocates each distinct key once,
19/// however many maps have it.
20#[derive(Clone, Default)]
21pub struct Map {
22    entries: Vec<(Arc<str>, Value)>,
23    /// Each key's position, once a lookup in a big map asked for it;
24    /// boxed, so a map (and so every [`Value`]) is no bigger for it.
25    index: OnceLock<Box<KeyIndex>>,
26}
27
28/// Each key's position among a map's entries.
29#[derive(Clone, Default)]
30struct KeyIndex(HashMap<Arc<str>, usize>);
31
32impl std::fmt::Debug for Map {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_tuple("Map").field(&self.entries).finish()
35    }
36}
37
38impl PartialEq for Map {
39    fn eq(&self, other: &Self) -> bool {
40        self.entries == other.entries
41    }
42}
43
44impl Map {
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    pub fn with_capacity(n: usize) -> Self {
50        Self {
51            entries: Vec::with_capacity(n),
52            index: OnceLock::new(),
53        }
54    }
55
56    /// Where `key` is: by the index in a big map (made now if it isn't
57    /// yet), by a scan in a small one.
58    fn position(&self, key: &str) -> Option<usize> {
59        if self.entries.len() < INDEXED_FROM {
60            return self.entries.iter().position(|(k, _)| &**k == key);
61        }
62        let index = self.index.get_or_init(|| {
63            Box::new(KeyIndex(
64                self.entries
65                    .iter()
66                    .enumerate()
67                    .map(|(i, (k, _))| (k.clone(), i))
68                    .collect(),
69            ))
70        });
71        index.0.get(key).copied()
72    }
73
74    /// The index, if one's made: what a change keeps current.
75    fn index_mut(&mut self) -> Option<&mut HashMap<Arc<str>, usize>> {
76        self.index.get_mut().map(|b| &mut b.0)
77    }
78
79    pub fn get(&self, key: &str) -> Option<&Value> {
80        self.position(key).map(|i| &self.entries[i].1)
81    }
82
83    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
84        let i = self.position(key)?;
85        Some(&mut self.entries[i].1)
86    }
87
88    /// The value of the `i`-th entry.
89    pub fn value_at(&self, i: usize) -> Option<&Value> {
90        self.entries.get(i).map(|(_, v)| v)
91    }
92
93    /// The position of `key` among the entries.
94    pub fn index_of(&self, key: &str) -> Option<usize> {
95        self.position(key)
96    }
97
98    /// Sets `key`, in place if it's there, else at the end; returns the old value.
99    pub fn insert(&mut self, key: impl Into<String>, value: Value) -> Option<Value> {
100        let key = key.into();
101        if let Some(slot) = self.get_mut(&key) {
102            return Some(std::mem::replace(slot, value));
103        }
104        self.push_unchecked(key.into(), value);
105        None
106    }
107
108    /// Appends without checking for the key — for decoders that already did.
109    pub(crate) fn push_unchecked(&mut self, key: Arc<str>, value: Value) {
110        let at = self.entries.len();
111        if let Some(index) = self.index_mut() {
112            index.insert(key.clone(), at);
113        }
114        self.entries.push((key, value));
115    }
116
117    pub fn remove(&mut self, key: &str) -> Option<Value> {
118        let i = self.position(key)?;
119        let (_, value) = self.entries.remove(i);
120        if let Some(index) = self.index_mut() {
121            index.remove(key);
122            for at in index.values_mut() {
123                if *at > i {
124                    *at -= 1;
125                }
126            }
127        }
128        Some(value)
129    }
130
131    pub fn len(&self) -> usize {
132        self.entries.len()
133    }
134
135    pub fn is_empty(&self) -> bool {
136        self.entries.is_empty()
137    }
138
139    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
140        self.entries.iter().map(|(k, v)| (&**k, v))
141    }
142
143    pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
144        self.entries.iter().map(|(k, _)| &**k)
145    }
146
147    pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
148        self.entries.iter().map(|(_, v)| v)
149    }
150
151    /// Keeps the entries `keep` says to, in one pass; the index made again
152    /// when it's next needed.
153    pub fn retain(&mut self, mut keep: impl FnMut(&str, &Value) -> bool) {
154        self.entries.retain(|(k, v)| keep(k, v));
155        self.index = OnceLock::new();
156    }
157}
158
159impl IntoIterator for Map {
160    type Item = (String, Value);
161    type IntoIter = std::iter::Map<
162        std::vec::IntoIter<(Arc<str>, Value)>,
163        fn((Arc<str>, Value)) -> (String, Value),
164    >;
165    fn into_iter(self) -> Self::IntoIter {
166        self.entries.into_iter().map(|(k, v)| (k.to_string(), v))
167    }
168}
169
170impl<'a> IntoIterator for &'a Map {
171    type Item = (&'a str, &'a Value);
172    type IntoIter = std::iter::Map<
173        std::slice::Iter<'a, (Arc<str>, Value)>,
174        fn(&'a (Arc<str>, Value)) -> (&'a str, &'a Value),
175    >;
176    fn into_iter(self) -> Self::IntoIter {
177        self.entries.iter().map(|(k, v)| (&**k, v))
178    }
179}
180
181impl<K: Into<String>> FromIterator<(K, Value)> for Map {
182    fn from_iter<I: IntoIterator<Item = (K, Value)>>(iter: I) -> Self {
183        let mut map = Map::new();
184        for (k, v) in iter {
185            map.insert(k, v);
186        }
187        map
188    }
189}
190
191#[cfg(test)]
192mod tests;