index_pool/
lib.rs

1//! A pool which manages allocation of unique indices. Acts like a
2//! psuedo-memory allocator.
3//!
4//! ```
5//! extern crate index_pool;
6//! use index_pool::IndexPool;
7//!
8//! fn main() {
9//!     let mut pool = IndexPool::new();
10//!
11//!     let a = pool.new_id();
12//!     let b = pool.new_id();
13//!     let c = pool.new_id();
14//!
15//!     let mut data = vec![""; pool.maximum()];
16//!     data[a] = "apple";
17//!     data[b] = "banana";
18//!     data[c] = "coconut";
19//!
20//!     // Nevermind, no bananas
21//!     pool.return_id(b).unwrap();
22//!
23//!     let p = pool.new_id();
24//!     data[p] = "pineapple";
25//!
26//!     assert_eq!(data, vec!["apple", "pineapple", "coconut"]);
27//! }
28//! ```
29
30extern crate free_ranges;
31
32use free_ranges::FreeRanges;
33use free_ranges::Range;
34
35use std::error::Error;
36use std::fmt;
37use std::usize;
38
39pub mod iter;
40
41/// A pool which manages allocation of unique indices. Acts like a
42/// psuedo-memory allocator.
43#[derive(Debug)]
44pub struct IndexPool {
45    next_id: usize,
46    in_use: usize,
47    free_list: FreeRanges,
48}
49
50impl IndexPool {
51    /// Constructs an empty IndexPool. Indices will start at `0`.
52    #[inline]
53    pub fn new() -> Self {
54        Self::with_initial_index(0)
55    }
56
57    /// Constructs an empty IndexPool. `index` will be the first
58    /// index returned from `new_id`. You can logically think of
59    /// this as either specifying a base index for the pool, or
60    /// pre-allocating the `[0..index)` range. This datastructure
61    /// does not care which is your usecase, and neither has any
62    /// kind of performance penalty, except that `in_use()` will
63    /// include the `[0..index)` range.
64    pub fn with_initial_index(index: usize) -> Self {
65        IndexPool {
66            next_id: index,
67            in_use: 0,
68            free_list: FreeRanges::new(),
69        }
70    }
71
72    /// Allocates a new index for use. This is guaranteed to not be any index
73    /// which has previously been returned from `new_id` but has not yet been
74    /// passed to `return_id`.
75    #[inline]
76    pub fn new_id(&mut self) -> usize {
77        self.in_use += 1;
78
79        if let Some(id) = self.free_list.set_first_used() {
80            return id;
81        }
82
83        let id = self.next_id;
84        self.next_id += 1;
85        return id;
86    }
87
88    #[inline]
89    /// Attempts to allocate a specific index
90    pub fn request_id(&mut self, id: usize) -> Result<(), AlreadyInUse> {
91        assert!(id < usize::MAX);
92        if id == self.next_id {
93            self.next_id += 1;
94            self.in_use += 1;
95            Ok(())
96        } else if id > self.next_id {
97            self.free_list.set_range_free(Range {
98                min: self.next_id,
99                max: id - 1,
100            });
101            self.next_id = id + 1;
102            self.in_use += 1;
103
104            Ok(())
105        } else if self.free_list.set_used(id) {
106            self.in_use += 1;
107            Ok(())
108        } else {
109            Err(AlreadyInUse)
110        }
111    }
112
113    /// Gives an Id back to the pool so that it may be handed out again.
114    /// Returns Err if the Id was not in use at the time. Whether ignoring
115    /// such an error is okay is up to your own usecase.
116    #[inline]
117    pub fn return_id(&mut self, id: usize) -> Result<(), AlreadyReturned> {
118        if id >= self.next_id {
119            return Err(AlreadyReturned);
120        }
121
122        if id + 1 == self.next_id {
123            self.next_id -= 1;
124        } else {
125            if !self.free_list.set_free(id) {
126                return Err(AlreadyReturned);
127            }
128            assert!(self.free_list.is_free(id));
129        }
130
131        self.in_use -= 1;
132
133        while self.collapse_next() {}
134
135        Ok(())
136    }
137
138    /// Returns an upper bound on the number of IDs which have been
139    /// allocated, specifically the `highest numbered ID in use + 1`.
140    /// Useful if you're going to e.g. create a Vec which has room
141    /// for all of your IDs.
142    #[inline]
143    pub fn maximum(&self) -> usize {
144        self.next_id
145    }
146
147    /// Returns the number of currently in-use indices
148    #[inline]
149    pub fn in_use(&self) -> usize {
150        self.in_use
151    }
152
153    #[inline]
154    /// Checks if a specific index is currently free
155    pub fn is_free(&self, id: usize) -> bool {
156        id >= self.next_id || self.free_list.is_free(id)
157    }
158
159    /// Returns an iterator over all indices which are in use
160    #[inline]
161    pub fn all_indices(&self) -> iter::IndexIter {
162        iter::IndexIter::new(self.free_list.free_ranges(), self.next_id)
163    }
164
165    #[inline]
166    pub fn all_indices_after(&self, after: usize) -> iter::IndexAfterIter {
167        iter::IndexAfterIter::new(self.free_list.free_ranges_after(after), after, self.next_id)
168    }
169
170    #[inline]
171    fn collapse_next(&mut self) -> bool {
172        if let Some(last_range) = self.free_list.free_ranges().rev().nth(0).cloned() {
173            if last_range.max + 1 == self.next_id {
174                self.free_list.remove_last_contiguous();
175                self.next_id = last_range.min;
176                return true;
177            }
178        }
179
180        false
181    }
182
183    #[inline]
184    pub fn clear(&mut self) {
185        self.free_list.clear();
186        self.in_use = 0;
187        self.next_id = 0;
188    }
189}
190
191impl Default for IndexPool {
192    /// Constructs an empty IndexPool. Indices will start at `0`.
193    #[inline]
194    fn default() -> Self {
195        IndexPool::new()
196    }
197}
198
199#[derive(Debug, PartialEq, Eq)]
200pub struct AlreadyReturned;
201
202impl fmt::Display for AlreadyReturned {
203    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
204        fmt.write_str(self.description())
205    }
206}
207
208impl Error for AlreadyReturned {
209    fn description(&self) -> &str {
210        "An index was tried to be returned to the pool, but it was already marked as free."
211    }
212}
213
214#[derive(Debug, PartialEq, Eq)]
215pub struct AlreadyInUse;
216
217impl fmt::Display for AlreadyInUse {
218    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
219        fmt.write_str(self.description())
220    }
221}
222
223impl Error for AlreadyInUse {
224    fn description(&self) -> &str {
225        "An index was requested which was already marked as in use."
226    }
227}