Skip to main content

legion_core/
permission.rs

1use smallvec::SmallVec;
2use std::fmt::{Debug, Display};
3
4#[derive(Clone)]
5pub struct Permissions<T: PartialEq> {
6    items: SmallVec<[T; 4]>,
7    shared: usize, // index of first shared
8    write: usize,  // index of first write exclusive
9}
10
11impl<T: PartialEq> Permissions<T> {
12    pub fn new() -> Self {
13        Self {
14            items: SmallVec::default(),
15            shared: 0,
16            write: 0,
17        }
18    }
19
20    fn find(&self, item: &T) -> Option<usize> { self.items.iter().position(|x| x == item) }
21
22    pub fn push(&mut self, item: T) {
23        if let Some(index) = self.find(&item) {
24            if index < self.shared {
25                // if it is in read exclusive, move it up into shared
26                self.items.swap(index, self.shared - 1);
27                self.shared -= 1;
28            } else if index > self.write {
29                // if it is in write exclusive, move it down into shared
30                self.items.swap(index, self.write);
31                self.write += 1;
32            }
33        } else {
34            // add the item
35            self.items.push(item);
36
37            // swap it down into shared
38            let index = self.items.len() - 1;
39            self.items.swap(index, self.write);
40            self.write += 1;
41        }
42    }
43
44    pub fn push_read(&mut self, item: T) {
45        if let Some(index) = self.find(&item) {
46            // if the item had exclusive write, move it into shared
47            if index >= self.write {
48                // swap it down to the beginning of the exclusive write segment,
49                // then move the boundry over it
50                self.items.swap(index, self.write);
51                self.write += 1;
52            }
53        } else {
54            // add the item to the end of the vec
55            self.items.push(item);
56            let index = self.items.len() - 1;
57
58            // move it down into shared
59            self.items.swap(index, self.write);
60
61            // move it down into read exclusive
62            self.items.swap(self.write, self.shared);
63
64            // move the boundaries
65            self.write += 1;
66            self.shared += 1;
67        }
68    }
69
70    pub fn push_write(&mut self, item: T) {
71        if let Some(index) = self.find(&item) {
72            if index < self.shared {
73                // move it into shared
74                self.items.swap(index, self.shared - 1);
75                self.shared -= 1;
76            }
77        } else {
78            // add the item to the end of the vec
79            self.items.push(item);
80        }
81    }
82
83    pub fn remove(&mut self, item: &T) {
84        if let Some(mut index) = self.find(item) {
85            if index < self.shared {
86                // push value up into shared
87                self.items.swap(index, self.shared - 1);
88                self.shared -= 1;
89                index = self.shared;
90            }
91
92            if index < self.write {
93                // push value up into write
94                self.items.swap(index, self.write - 1);
95                self.write -= 1;
96                index = self.write;
97            }
98
99            self.items.swap_remove(index);
100        }
101    }
102
103    pub fn remove_read(&mut self, item: &T) {
104        if let Some(index) = self.find(item) {
105            if index < self.shared {
106                // move into shared
107                self.items.swap(index, self.shared - 1);
108                self.shared -= 1;
109
110                // move into write
111                self.items.swap(self.shared, self.write - 1);
112                self.write -= 1;
113
114                // remove
115                self.items.swap_remove(self.write);
116            } else if index < self.write {
117                // move into write-only
118                self.items.swap(index, self.write - 1);
119                self.write -= 1;
120            }
121        }
122    }
123
124    pub fn remove_write(&mut self, item: &T) {
125        if let Some(index) = self.find(item) {
126            if index >= self.write {
127                // remove
128                self.items.swap_remove(index);
129            } else if index >= self.shared {
130                // move into read-only
131                self.items.swap(index, self.shared);
132                self.shared += 1;
133            }
134        }
135    }
136
137    pub fn add(&mut self, mut other: Self) {
138        for read in other.items.drain(..other.shared) {
139            self.push_read(read);
140        }
141
142        for shared in other.items.drain(..(other.write - other.shared)) {
143            self.push(shared);
144        }
145
146        for write in other.items.drain(..) {
147            self.push_write(write);
148        }
149    }
150
151    pub fn subtract(&mut self, other: &Self) {
152        for read in other.read_only() {
153            self.remove_read(read);
154        }
155
156        for shared in other.readwrite() {
157            self.remove(shared);
158        }
159
160        for write in other.write_only() {
161            self.remove_write(write);
162        }
163    }
164
165    pub fn reads(&self) -> &[T] { &self.items[..self.write] }
166
167    pub fn writes(&self) -> &[T] { &self.items[self.shared..] }
168
169    pub fn read_only(&self) -> &[T] { &self.items[..self.shared] }
170
171    pub fn write_only(&self) -> &[T] { &self.items[self.write..] }
172
173    pub fn readwrite(&self) -> &[T] { &self.items[self.shared..self.write] }
174
175    pub fn is_superset(&self, other: &Self) -> bool {
176        for read in other.read_only() {
177            // exit if reads are in exclusive write range, or are not found
178            if self.find(read).map(|i| i >= self.write).unwrap_or(true) {
179                return false;
180            }
181        }
182
183        for shared in other.readwrite() {
184            // exit if shareds are in exclusive read or write range, or are not found
185            if self
186                .find(shared)
187                .map(|i| i < self.shared || i >= self.write)
188                .unwrap_or(true)
189            {
190                return false;
191            }
192        }
193
194        for write in other.write_only() {
195            // exit if writes are in exclusive read range, or are not found
196            if self.find(write).map(|i| i < self.shared).unwrap_or(true) {
197                return false;
198            }
199        }
200
201        true
202    }
203
204    pub fn is_disjoint(&self, other: &Self) -> bool {
205        for read in other.read_only() {
206            // exit if reads are in read-only or shared range
207            if self.find(read).map(|i| i < self.write).unwrap_or(false) {
208                return false;
209            }
210        }
211
212        for shared in other.readwrite() {
213            // exit if shareds are found
214            if self.find(shared).is_some() {
215                return false;
216            }
217        }
218
219        for write in other.write_only() {
220            // exit if writes are in write-only or shared range
221            if self.find(write).map(|i| i >= self.shared).unwrap_or(false) {
222                return false;
223            }
224        }
225
226        true
227    }
228}
229
230impl<T: PartialEq> Default for Permissions<T> {
231    fn default() -> Self { Self::new() }
232}
233
234impl<T: PartialEq + Debug> Debug for Permissions<T> {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        fn list<V: Debug>(items: &[V]) -> String {
237            use itertools::Itertools;
238            items
239                .iter()
240                .map(|x| format!("{:?}", x))
241                .fold1(|x, y| format!("{}, {}", x, y))
242                .unwrap_or_else(|| "".to_owned())
243        }
244
245        write!(
246            f,
247            "Permissions {{ reads: [{}], writes: [{}] }}",
248            list::<T>(&self.reads()),
249            list::<T>(&self.writes())
250        )
251    }
252}
253
254impl<T: PartialEq + Display> Display for Permissions<T> {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        fn list<V: Display>(items: &[V]) -> String {
257            use itertools::Itertools;
258            items
259                .iter()
260                .map(|x| format!("{}", x))
261                .fold1(|x, y| format!("{}, {}", x, y))
262                .unwrap_or_else(|| "".to_owned())
263        }
264
265        write!(
266            f,
267            "reads: [{}], writes: [{}]",
268            list::<T>(&self.reads()),
269            list::<T>(&self.writes())
270        )
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::Permissions;
277
278    #[test]
279    fn push_read() {
280        let mut permissions = Permissions::new();
281        permissions.push_read(1usize);
282
283        let empty: &[usize] = &[];
284        assert_eq!(permissions.read_only(), &[1usize]);
285        assert_eq!(permissions.readwrite(), empty);
286        assert_eq!(permissions.write_only(), empty);
287    }
288
289    #[test]
290    fn push_write() {
291        let mut permissions = Permissions::new();
292        permissions.push_write(1usize);
293
294        let empty: &[usize] = &[];
295        assert_eq!(permissions.read_only(), empty);
296        assert_eq!(permissions.readwrite(), empty);
297        assert_eq!(permissions.write_only(), &[1usize]);
298    }
299
300    #[test]
301    fn push_both() {
302        let mut permissions = Permissions::new();
303        permissions.push(1usize);
304
305        let empty: &[usize] = &[];
306        assert_eq!(permissions.read_only(), empty);
307        assert_eq!(permissions.readwrite(), &[1usize]);
308        assert_eq!(permissions.write_only(), empty);
309    }
310
311    #[test]
312    fn promote_read_to_readwrite() {
313        let mut permissions = Permissions::new();
314        permissions.push_read(1usize);
315        permissions.push_write(1usize);
316
317        let empty: &[usize] = &[];
318        assert_eq!(permissions.read_only(), empty);
319        assert_eq!(permissions.readwrite(), &[1usize]);
320        assert_eq!(permissions.write_only(), empty);
321    }
322
323    #[test]
324    fn promote_write_to_readwrite() {
325        let mut permissions = Permissions::new();
326        permissions.push_write(1usize);
327        permissions.push_read(1usize);
328
329        let empty: &[usize] = &[];
330        assert_eq!(permissions.read_only(), empty);
331        assert_eq!(permissions.readwrite(), &[1usize]);
332        assert_eq!(permissions.write_only(), empty);
333    }
334
335    #[test]
336    fn remove_write() {
337        let mut permissions = Permissions::new();
338        permissions.push_write(1usize);
339        permissions.remove_write(&1usize);
340
341        let empty: &[usize] = &[];
342        assert_eq!(permissions.read_only(), empty);
343        assert_eq!(permissions.readwrite(), empty);
344        assert_eq!(permissions.write_only(), empty);
345    }
346
347    #[test]
348    fn remove_read() {
349        let mut permissions = Permissions::new();
350        permissions.push_read(1usize);
351        permissions.remove_read(&1usize);
352
353        let empty: &[usize] = &[];
354        assert_eq!(permissions.read_only(), empty);
355        assert_eq!(permissions.readwrite(), empty);
356        assert_eq!(permissions.write_only(), empty);
357    }
358
359    #[test]
360    fn demote_readwrite_to_read() {
361        let mut permissions = Permissions::new();
362        permissions.push(1usize);
363        permissions.remove_write(&1usize);
364
365        let empty: &[usize] = &[];
366        assert_eq!(permissions.read_only(), &[1usize]);
367        assert_eq!(permissions.readwrite(), empty);
368        assert_eq!(permissions.write_only(), empty);
369    }
370
371    #[test]
372    fn demote_readwrite_to_write() {
373        let mut permissions = Permissions::new();
374        permissions.push(1usize);
375        permissions.remove_read(&1usize);
376
377        let empty: &[usize] = &[];
378        assert_eq!(permissions.read_only(), empty);
379        assert_eq!(permissions.readwrite(), empty);
380        assert_eq!(permissions.write_only(), &[1usize]);
381    }
382}