1use std::cmp;
9use std::fmt;
10use std::sync::atomic::AtomicU32;
11use std::sync::atomic::AtomicU64;
12use std::sync::atomic::Ordering::Acquire;
13use std::sync::atomic::Ordering::Relaxed;
14use std::sync::atomic::Ordering::Release;
15use std::sync::Arc;
16
17use bitflags::bitflags;
18
19use crate::ops::DagAlgorithm;
20use crate::ops::IdConvert;
21use crate::Id;
22use crate::VerLink;
23
24bitflags! {
25 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
26 pub struct Flags: u32 {
27 const FULL = 0x1;
29
30 const EMPTY = 0x2;
32
33 const ID_DESC = 0x4;
35
36 const ID_ASC = 0x8;
38
39 const TOPO_DESC = 0x10;
41
42 const HAS_MIN_ID = 0x20;
44
45 const HAS_MAX_ID = 0x40;
47
48 const FILTER = 0x80;
50
51 const ANCESTORS = 0x100;
53 }
54}
55
56#[derive(Default)]
58pub struct Hints {
59 flags: AtomicU32,
61 min_id: AtomicU64,
62 max_id: AtomicU64,
63 id_map: IdMapSnapshot,
64 dag: DagSnapshot,
65}
66
67impl Hints {
68 pub fn new_with_idmap_dag(
69 id_map: impl Into<IdMapSnapshot>,
70 dag: impl Into<DagSnapshot>,
71 ) -> Self {
72 Self {
73 id_map: id_map.into(),
74 dag: dag.into(),
75 ..Default::default()
76 }
77 }
78
79 pub fn new_inherit_idmap_dag(hints: &Self) -> Self {
80 Self::new_with_idmap_dag(hints, hints)
81 }
82
83 pub fn union(hints_list: &[&Self]) -> Self {
87 let default = Self::default();
88 debug_assert!(default.id_map().is_none());
90 let id_map = hints_list
91 .iter()
92 .fold(Some(&default), |opt_a, b| {
93 opt_a.and_then(
94 |a| match a.id_map_version().partial_cmp(&b.id_map_version()) {
95 None => None, Some(cmp::Ordering::Equal) | Some(cmp::Ordering::Greater) => Some(a),
97 Some(cmp::Ordering::Less) => Some(b),
98 },
99 )
100 })
101 .and_then(|a| a.id_map());
102 debug_assert!(default.dag().is_none());
104 let dag = hints_list
105 .iter()
106 .fold(Some(&default), |opt_a, b| {
107 opt_a.and_then(|a| match a.dag_version().partial_cmp(&b.dag_version()) {
108 None => None,
109 Some(cmp::Ordering::Equal) | Some(cmp::Ordering::Greater) => Some(a),
110 Some(cmp::Ordering::Less) => Some(b),
111 })
112 })
113 .and_then(|a| a.dag());
114 Self {
115 id_map: IdMapSnapshot(id_map),
116 dag: DagSnapshot(dag),
117 ..Self::default()
118 }
119 }
120
121 pub fn flags(&self) -> Flags {
122 let flags = self.flags.load(Relaxed);
123 Flags::from_bits_truncate(flags)
124 }
125
126 pub fn contains(&self, flags: Flags) -> bool {
127 self.flags().contains(flags)
128 }
129
130 pub fn min_id(&self) -> Option<Id> {
131 if self.contains(Flags::HAS_MIN_ID) {
132 Some(Id(self.min_id.load(Acquire)))
133 } else {
134 None
135 }
136 }
137
138 pub fn max_id(&self) -> Option<Id> {
139 if self.contains(Flags::HAS_MAX_ID) {
140 Some(Id(self.max_id.load(Acquire)))
141 } else {
142 None
143 }
144 }
145
146 pub fn update_flags_with(&self, func: impl Fn(Flags) -> Flags) -> &Self {
147 let mut flags = func(self.flags());
148 if flags.contains(Flags::EMPTY) {
150 flags.insert(Flags::ID_ASC | Flags::ID_DESC | Flags::TOPO_DESC | Flags::ANCESTORS);
151 }
152 if flags.contains(Flags::FULL) {
153 flags.insert(Flags::ANCESTORS);
154 }
155 self.flags.store(flags.bits(), Relaxed);
156 self
157 }
158
159 pub fn add_flags(&self, flags: Flags) -> &Self {
160 self.update_flags_with(|f| f | flags);
161 self
162 }
163
164 pub fn remove_flags(&self, flags: Flags) -> &Self {
165 self.update_flags_with(|f| f - flags);
166 self
167 }
168
169 pub fn set_min_id(&self, min_id: Id) -> &Self {
170 self.min_id.store(min_id.0, Release);
171 self.add_flags(Flags::HAS_MIN_ID);
172 self
173 }
174
175 pub fn set_max_id(&self, max_id: Id) -> &Self {
176 self.max_id.store(max_id.0, Release);
177 self.add_flags(Flags::HAS_MAX_ID);
178 self
179 }
180
181 pub fn inherit_flags_min_max_id(&self, other: &Hints) -> &Self {
182 self.update_flags_with(|_| other.flags());
183 if let Some(id) = other.min_id() {
184 self.set_min_id(id);
185 }
186 if let Some(id) = other.max_id() {
187 self.set_max_id(id);
188 }
189 self
190 }
191
192 pub fn dag(&self) -> Option<Arc<dyn DagAlgorithm + Send + Sync>> {
193 self.dag.0.clone()
194 }
195
196 pub fn id_map(&self) -> Option<Arc<dyn IdConvert + Send + Sync>> {
197 self.id_map.0.clone()
198 }
199
200 pub fn dag_version(&self) -> Option<&VerLink> {
202 self.dag.0.as_ref().map(|d| d.dag_version())
203 }
204
205 pub fn id_map_version(&self) -> Option<&VerLink> {
207 self.id_map.0.as_ref().map(|d| d.map_version())
208 }
209}
210
211#[derive(Clone, Default)]
212pub struct DagSnapshot(Option<Arc<dyn DagAlgorithm + Send + Sync>>);
213
214impl From<&Hints> for DagSnapshot {
215 fn from(hints: &Hints) -> Self {
216 hints.dag.clone()
217 }
218}
219
220impl From<Arc<dyn DagAlgorithm + Send + Sync>> for DagSnapshot {
221 fn from(dag: Arc<dyn DagAlgorithm + Send + Sync>) -> Self {
222 DagSnapshot(Some(dag))
223 }
224}
225
226#[derive(Clone, Default)]
227pub struct IdMapSnapshot(Option<Arc<dyn IdConvert + Send + Sync>>);
228
229impl From<&Hints> for IdMapSnapshot {
230 fn from(hints: &Hints) -> Self {
231 hints.id_map.clone()
232 }
233}
234
235impl From<Arc<dyn IdConvert + Send + Sync>> for IdMapSnapshot {
236 fn from(dag: Arc<dyn IdConvert + Send + Sync>) -> Self {
237 IdMapSnapshot(Some(dag))
238 }
239}
240
241#[derive(Clone, Default)]
242pub struct DagVersion<'a>(Option<&'a VerLink>);
243
244impl<'a> From<&'a Hints> for DagVersion<'a> {
245 fn from(hints: &'a Hints) -> Self {
246 Self(match &hints.dag.0 {
247 Some(d) => Some(d.dag_version()),
248 None => None,
249 })
250 }
251}
252
253impl<'a> From<&'a VerLink> for DagVersion<'a> {
254 fn from(version: &'a VerLink) -> Self {
255 Self(Some(version))
256 }
257}
258
259#[derive(Clone, Default)]
260pub struct IdMapVersion<'a>(Option<&'a VerLink>);
261
262impl<'a> From<&'a Hints> for IdMapVersion<'a> {
263 fn from(hints: &'a Hints) -> Self {
264 Self(match &hints.id_map.0 {
265 Some(m) => Some(m.map_version()),
266 None => None,
267 })
268 }
269}
270
271impl<'a> From<&'a VerLink> for IdMapVersion<'a> {
272 fn from(version: &'a VerLink) -> Self {
273 Self(Some(version))
274 }
275}
276
277impl Clone for Hints {
278 fn clone(&self) -> Self {
279 Self {
280 flags: AtomicU32::new(self.flags.load(Acquire)),
281 min_id: AtomicU64::new(self.min_id.load(Acquire)),
282 max_id: AtomicU64::new(self.max_id.load(Acquire)),
283 id_map: self.id_map.clone(),
284 dag: self.dag.clone(),
285 }
286 }
287}
288
289impl fmt::Debug for Hints {
290 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
291 write!(
292 f,
293 "Hints({:?}",
294 self.flags() - (Flags::HAS_MIN_ID | Flags::HAS_MAX_ID)
295 )?;
296 match (self.min_id(), self.max_id()) {
297 (Some(min), Some(max)) => write!(f, ", {}..={}", min.0, max.0)?,
298 (Some(min), None) => write!(f, ", {}..", min.0)?,
299 (None, Some(max)) => write!(f, ", ..={}", max.0)?,
300 (None, None) => {}
301 }
302 write!(f, ")")?;
303 Ok(())
304 }
305}
306
307#[cfg(test)]
308#[test]
309fn test_incompatilbe_union() {
310 use crate::tests::dummy_dag::DummyDag;
311 let dag1 = DummyDag::new();
312 let dag2 = DummyDag::new();
313
314 let mut hints1 = Hints::default();
315 hints1.dag = DagSnapshot(Some(dag1.dag_snapshot().unwrap()));
316
317 let mut hints2 = Hints::default();
318 hints2.dag = DagSnapshot(Some(dag2.dag_snapshot().unwrap()));
319
320 assert_eq!(
321 Hints::union(&[&hints1, &hints1]).dag_version(),
322 hints1.dag_version()
323 );
324
325 assert_eq!(Hints::union(&[&hints1, &hints2]).dag_version(), None);
326 assert_eq!(Hints::union(&[&hints2, &hints1]).dag_version(), None);
327 assert_eq!(
328 Hints::union(&[&hints2, &hints1, &hints2]).dag_version(),
329 None
330 );
331}