]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! A structure for holding a set of enum variants. | |
12 | //! | |
13 | //! This module defines a container which uses an efficient bit mask | |
14 | //! representation to hold C-like enum variants. | |
15 | ||
62682a34 SL |
16 | #![unstable(feature = "enumset", |
17 | reason = "matches collection reform specification, \ | |
e9174d1e SL |
18 | waiting for dust to settle", |
19 | issue = "0")] | |
62682a34 | 20 | |
85aaf69f | 21 | use core::marker; |
1a4d82fc | 22 | use core::fmt; |
92a42be0 | 23 | use core::iter::FromIterator; |
1a4d82fc JJ |
24 | use core::ops::{Sub, BitOr, BitAnd, BitXor}; |
25 | ||
62682a34 SL |
26 | // FIXME(contentions): implement union family of methods? (general design may be |
27 | // wrong here) | |
1a4d82fc | 28 | |
1a4d82fc | 29 | /// A specialized set implementation to use enum types. |
c34b1796 | 30 | /// |
62682a34 SL |
31 | /// It is a logic error for an item to be modified in such a way that the |
32 | /// transformation of the item to or from a `usize`, as determined by the | |
33 | /// `CLike` trait, changes while the item is in the set. This is normally only | |
34 | /// possible through `Cell`, `RefCell`, global state, I/O, or unsafe code. | |
35 | #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] | |
1a4d82fc JJ |
36 | pub struct EnumSet<E> { |
37 | // We must maintain the invariant that no bits are set | |
38 | // for which no variant exists | |
85aaf69f SL |
39 | bits: usize, |
40 | marker: marker::PhantomData<E>, | |
1a4d82fc JJ |
41 | } |
42 | ||
43 | impl<E> Copy for EnumSet<E> {} | |
44 | ||
c34b1796 | 45 | impl<E> Clone for EnumSet<E> { |
92a42be0 SL |
46 | fn clone(&self) -> EnumSet<E> { |
47 | *self | |
48 | } | |
c34b1796 AL |
49 | } |
50 | ||
85aaf69f | 51 | #[stable(feature = "rust1", since = "1.0.0")] |
92a42be0 | 52 | impl<E: CLike + fmt::Debug> fmt::Debug for EnumSet<E> { |
1a4d82fc | 53 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
b039eaaf | 54 | fmt.debug_set().entries(self).finish() |
1a4d82fc JJ |
55 | } |
56 | } | |
57 | ||
85aaf69f | 58 | /// An interface for casting C-like enum to usize and back. |
1a4d82fc JJ |
59 | /// A typically implementation is as below. |
60 | /// | |
61 | /// ```{rust,ignore} | |
85aaf69f | 62 | /// #[repr(usize)] |
1a4d82fc JJ |
63 | /// enum Foo { |
64 | /// A, B, C | |
65 | /// } | |
66 | /// | |
67 | /// impl CLike for Foo { | |
85aaf69f SL |
68 | /// fn to_usize(&self) -> usize { |
69 | /// *self as usize | |
1a4d82fc JJ |
70 | /// } |
71 | /// | |
85aaf69f | 72 | /// fn from_usize(v: usize) -> Foo { |
1a4d82fc JJ |
73 | /// unsafe { mem::transmute(v) } |
74 | /// } | |
75 | /// } | |
76 | /// ``` | |
77 | pub trait CLike { | |
85aaf69f SL |
78 | /// Converts a C-like enum to a `usize`. |
79 | fn to_usize(&self) -> usize; | |
80 | /// Converts a `usize` to a C-like enum. | |
81 | fn from_usize(usize) -> Self; | |
1a4d82fc JJ |
82 | } |
83 | ||
92a42be0 | 84 | fn bit<E: CLike>(e: &E) -> usize { |
54a0048b | 85 | use core::mem; |
85aaf69f | 86 | let value = e.to_usize(); |
54a0048b SL |
87 | let bits = mem::size_of::<usize>() * 8; |
88 | assert!(value < bits, | |
92a42be0 | 89 | "EnumSet only supports up to {} variants.", |
54a0048b | 90 | bits - 1); |
1a4d82fc JJ |
91 | 1 << value |
92 | } | |
93 | ||
92a42be0 | 94 | impl<E: CLike> EnumSet<E> { |
1a4d82fc | 95 | /// Returns an empty `EnumSet`. |
1a4d82fc | 96 | pub fn new() -> EnumSet<E> { |
92a42be0 SL |
97 | EnumSet { |
98 | bits: 0, | |
99 | marker: marker::PhantomData, | |
100 | } | |
1a4d82fc JJ |
101 | } |
102 | ||
103 | /// Returns the number of elements in the given `EnumSet`. | |
85aaf69f | 104 | pub fn len(&self) -> usize { |
c34b1796 | 105 | self.bits.count_ones() as usize |
1a4d82fc JJ |
106 | } |
107 | ||
108 | /// Returns true if the `EnumSet` is empty. | |
1a4d82fc JJ |
109 | pub fn is_empty(&self) -> bool { |
110 | self.bits == 0 | |
111 | } | |
112 | ||
113 | pub fn clear(&mut self) { | |
114 | self.bits = 0; | |
115 | } | |
116 | ||
117 | /// Returns `false` if the `EnumSet` contains any enum of the given `EnumSet`. | |
1a4d82fc JJ |
118 | pub fn is_disjoint(&self, other: &EnumSet<E>) -> bool { |
119 | (self.bits & other.bits) == 0 | |
120 | } | |
121 | ||
122 | /// Returns `true` if a given `EnumSet` is included in this `EnumSet`. | |
1a4d82fc JJ |
123 | pub fn is_superset(&self, other: &EnumSet<E>) -> bool { |
124 | (self.bits & other.bits) == other.bits | |
125 | } | |
126 | ||
127 | /// Returns `true` if this `EnumSet` is included in the given `EnumSet`. | |
1a4d82fc JJ |
128 | pub fn is_subset(&self, other: &EnumSet<E>) -> bool { |
129 | other.is_superset(self) | |
130 | } | |
131 | ||
132 | /// Returns the union of both `EnumSets`. | |
133 | pub fn union(&self, e: EnumSet<E>) -> EnumSet<E> { | |
92a42be0 SL |
134 | EnumSet { |
135 | bits: self.bits | e.bits, | |
136 | marker: marker::PhantomData, | |
137 | } | |
1a4d82fc JJ |
138 | } |
139 | ||
140 | /// Returns the intersection of both `EnumSets`. | |
141 | pub fn intersection(&self, e: EnumSet<E>) -> EnumSet<E> { | |
92a42be0 SL |
142 | EnumSet { |
143 | bits: self.bits & e.bits, | |
144 | marker: marker::PhantomData, | |
145 | } | |
1a4d82fc JJ |
146 | } |
147 | ||
148 | /// Adds an enum to the `EnumSet`, and returns `true` if it wasn't there before | |
1a4d82fc JJ |
149 | pub fn insert(&mut self, e: E) -> bool { |
150 | let result = !self.contains(&e); | |
151 | self.bits |= bit(&e); | |
152 | result | |
153 | } | |
154 | ||
155 | /// Removes an enum from the EnumSet | |
1a4d82fc JJ |
156 | pub fn remove(&mut self, e: &E) -> bool { |
157 | let result = self.contains(e); | |
158 | self.bits &= !bit(e); | |
159 | result | |
160 | } | |
161 | ||
162 | /// Returns `true` if an `EnumSet` contains a given enum. | |
1a4d82fc JJ |
163 | pub fn contains(&self, e: &E) -> bool { |
164 | (self.bits & bit(e)) != 0 | |
165 | } | |
166 | ||
167 | /// Returns an iterator over an `EnumSet`. | |
1a4d82fc JJ |
168 | pub fn iter(&self) -> Iter<E> { |
169 | Iter::new(self.bits) | |
170 | } | |
171 | } | |
172 | ||
92a42be0 | 173 | impl<E: CLike> Sub for EnumSet<E> { |
1a4d82fc JJ |
174 | type Output = EnumSet<E>; |
175 | ||
176 | fn sub(self, e: EnumSet<E>) -> EnumSet<E> { | |
92a42be0 SL |
177 | EnumSet { |
178 | bits: self.bits & !e.bits, | |
179 | marker: marker::PhantomData, | |
180 | } | |
1a4d82fc JJ |
181 | } |
182 | } | |
183 | ||
92a42be0 | 184 | impl<E: CLike> BitOr for EnumSet<E> { |
1a4d82fc JJ |
185 | type Output = EnumSet<E>; |
186 | ||
187 | fn bitor(self, e: EnumSet<E>) -> EnumSet<E> { | |
92a42be0 SL |
188 | EnumSet { |
189 | bits: self.bits | e.bits, | |
190 | marker: marker::PhantomData, | |
191 | } | |
1a4d82fc JJ |
192 | } |
193 | } | |
194 | ||
92a42be0 | 195 | impl<E: CLike> BitAnd for EnumSet<E> { |
1a4d82fc JJ |
196 | type Output = EnumSet<E>; |
197 | ||
198 | fn bitand(self, e: EnumSet<E>) -> EnumSet<E> { | |
92a42be0 SL |
199 | EnumSet { |
200 | bits: self.bits & e.bits, | |
201 | marker: marker::PhantomData, | |
202 | } | |
1a4d82fc JJ |
203 | } |
204 | } | |
205 | ||
92a42be0 | 206 | impl<E: CLike> BitXor for EnumSet<E> { |
1a4d82fc JJ |
207 | type Output = EnumSet<E>; |
208 | ||
209 | fn bitxor(self, e: EnumSet<E>) -> EnumSet<E> { | |
92a42be0 SL |
210 | EnumSet { |
211 | bits: self.bits ^ e.bits, | |
212 | marker: marker::PhantomData, | |
213 | } | |
1a4d82fc JJ |
214 | } |
215 | } | |
216 | ||
217 | /// An iterator over an EnumSet | |
218 | pub struct Iter<E> { | |
85aaf69f SL |
219 | index: usize, |
220 | bits: usize, | |
221 | marker: marker::PhantomData<E>, | |
1a4d82fc JJ |
222 | } |
223 | ||
224 | // FIXME(#19839) Remove in favor of `#[derive(Clone)]` | |
225 | impl<E> Clone for Iter<E> { | |
226 | fn clone(&self) -> Iter<E> { | |
227 | Iter { | |
228 | index: self.index, | |
229 | bits: self.bits, | |
85aaf69f | 230 | marker: marker::PhantomData, |
1a4d82fc JJ |
231 | } |
232 | } | |
233 | } | |
234 | ||
92a42be0 | 235 | impl<E: CLike> Iter<E> { |
85aaf69f | 236 | fn new(bits: usize) -> Iter<E> { |
92a42be0 SL |
237 | Iter { |
238 | index: 0, | |
239 | bits: bits, | |
240 | marker: marker::PhantomData, | |
241 | } | |
1a4d82fc JJ |
242 | } |
243 | } | |
244 | ||
92a42be0 | 245 | impl<E: CLike> Iterator for Iter<E> { |
1a4d82fc JJ |
246 | type Item = E; |
247 | ||
248 | fn next(&mut self) -> Option<E> { | |
249 | if self.bits == 0 { | |
250 | return None; | |
251 | } | |
252 | ||
253 | while (self.bits & 1) == 0 { | |
254 | self.index += 1; | |
255 | self.bits >>= 1; | |
256 | } | |
85aaf69f | 257 | let elem = CLike::from_usize(self.index); |
1a4d82fc JJ |
258 | self.index += 1; |
259 | self.bits >>= 1; | |
260 | Some(elem) | |
261 | } | |
262 | ||
85aaf69f | 263 | fn size_hint(&self) -> (usize, Option<usize>) { |
c34b1796 | 264 | let exact = self.bits.count_ones() as usize; |
1a4d82fc JJ |
265 | (exact, Some(exact)) |
266 | } | |
267 | } | |
268 | ||
92a42be0 SL |
269 | impl<E: CLike> FromIterator<E> for EnumSet<E> { |
270 | fn from_iter<I: IntoIterator<Item = E>>(iter: I) -> EnumSet<E> { | |
1a4d82fc | 271 | let mut ret = EnumSet::new(); |
85aaf69f | 272 | ret.extend(iter); |
1a4d82fc JJ |
273 | ret |
274 | } | |
275 | } | |
276 | ||
85aaf69f | 277 | #[stable(feature = "rust1", since = "1.0.0")] |
92a42be0 SL |
278 | impl<'a, E> IntoIterator for &'a EnumSet<E> where E: CLike |
279 | { | |
85aaf69f SL |
280 | type Item = E; |
281 | type IntoIter = Iter<E>; | |
282 | ||
283 | fn into_iter(self) -> Iter<E> { | |
284 | self.iter() | |
285 | } | |
286 | } | |
287 | ||
92a42be0 SL |
288 | impl<E: CLike> Extend<E> for EnumSet<E> { |
289 | fn extend<I: IntoIterator<Item = E>>(&mut self, iter: I) { | |
85aaf69f | 290 | for element in iter { |
1a4d82fc JJ |
291 | self.insert(element); |
292 | } | |
293 | } | |
294 | } | |
62682a34 SL |
295 | |
296 | #[stable(feature = "extend_ref", since = "1.2.0")] | |
297 | impl<'a, E: 'a + CLike + Copy> Extend<&'a E> for EnumSet<E> { | |
92a42be0 | 298 | fn extend<I: IntoIterator<Item = &'a E>>(&mut self, iter: I) { |
62682a34 SL |
299 | self.extend(iter.into_iter().cloned()); |
300 | } | |
301 | } |