1use std::marker::PhantomData;
2
3#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct Idx<T> {
6 raw: u32,
7 marker: PhantomData<fn() -> T>,
8}
9
10impl<T> Idx<T> {
11 pub fn new(index: usize) -> Self {
13 let raw =
14 u32::try_from(index).unwrap_or_else(|err| panic!("arena index must fit in u32: {err}"));
15 Self::from_raw(raw)
16 }
17
18 pub const fn from_raw(raw: u32) -> Self {
20 Self {
21 raw,
22 marker: PhantomData,
23 }
24 }
25
26 pub const fn index(self) -> usize {
28 self.raw as usize
29 }
30
31 pub const fn raw(self) -> u32 {
33 self.raw
34 }
35}
36
37impl<T> Clone for Idx<T> {
38 fn clone(&self) -> Self {
39 *self
40 }
41}
42
43impl<T> Copy for Idx<T> {}
44
45#[derive(Debug, PartialEq, Eq, Hash)]
47pub struct IdRange<T> {
48 start: u32,
49 len: u32,
50 marker: PhantomData<fn() -> T>,
51}
52
53impl<T> IdRange<T> {
54 pub const fn empty() -> Self {
56 Self {
57 start: 0,
58 len: 0,
59 marker: PhantomData,
60 }
61 }
62
63 pub fn new(start: Idx<T>, len: usize) -> Self {
65 Self::from_start_len(start.index(), len)
66 }
67
68 pub fn from_start_len(start: usize, len: usize) -> Self {
70 let end = start
71 .checked_add(len)
72 .unwrap_or_else(|| panic!("arena range end must not overflow usize"));
73 if end > u32::MAX as usize {
74 panic!("arena range end must fit in u32");
75 }
76 let start = u32::try_from(start)
77 .unwrap_or_else(|err| panic!("arena range start must fit in u32: {err}"));
78 let len = u32::try_from(len)
79 .unwrap_or_else(|err| panic!("arena range length must fit in u32: {err}"));
80 Self {
81 start,
82 len,
83 marker: PhantomData,
84 }
85 }
86
87 pub const fn start(self) -> Idx<T> {
89 Idx::from_raw(self.start)
90 }
91
92 pub const fn start_index(self) -> usize {
94 self.start as usize
95 }
96
97 pub const fn end_index(self) -> usize {
99 self.start as usize + self.len as usize
100 }
101
102 pub const fn len(self) -> usize {
104 self.len as usize
105 }
106
107 pub const fn is_empty(self) -> bool {
109 self.len == 0
110 }
111
112 pub fn slice(self, store: &[T]) -> &[T] {
114 &store[self.start_index()..self.end_index()]
115 }
116
117 pub fn slice_mut(self, store: &mut [T]) -> &mut [T] {
119 &mut store[self.start_index()..self.end_index()]
120 }
121}
122
123impl<T> Clone for IdRange<T> {
124 fn clone(&self) -> Self {
125 *self
126 }
127}
128
129impl<T> Copy for IdRange<T> {}
130
131impl<T> Default for IdRange<T> {
132 fn default() -> Self {
133 Self::empty()
134 }
135}
136
137#[derive(Debug, Clone, Default)]
139pub struct ListArena<T> {
140 items: Vec<T>,
141}
142
143impl<T> ListArena<T> {
144 pub const fn new() -> Self {
146 Self { items: Vec::new() }
147 }
148
149 pub fn with_capacity(capacity: usize) -> Self {
151 Self {
152 items: Vec::with_capacity(capacity),
153 }
154 }
155
156 pub fn push(&mut self, item: T) -> Idx<T> {
158 let id = Idx::new(self.items.len());
159 self.items.push(item);
160 self.check_len();
161 id
162 }
163
164 pub fn push_many<I>(&mut self, items: I) -> IdRange<T>
166 where
167 I: IntoIterator<Item = T>,
168 {
169 let start = self.items.len();
170 self.items.extend(items);
171 self.check_len();
172 IdRange::from_start_len(start, self.items.len() - start)
173 }
174
175 pub fn as_slice(&self) -> &[T] {
177 &self.items
178 }
179
180 pub fn as_mut_slice(&mut self) -> &mut [T] {
182 &mut self.items
183 }
184
185 pub fn get(&self, range: IdRange<T>) -> &[T] {
187 range.slice(&self.items)
188 }
189
190 pub fn get_mut(&mut self, range: IdRange<T>) -> &mut [T] {
192 range.slice_mut(&mut self.items)
193 }
194
195 pub fn len(&self) -> usize {
197 self.items.len()
198 }
199
200 pub fn is_empty(&self) -> bool {
202 self.items.is_empty()
203 }
204
205 pub fn into_vec(self) -> Vec<T> {
207 self.items
208 }
209
210 pub fn into_boxed_slice(self) -> Box<[T]> {
212 self.items.into_boxed_slice()
213 }
214
215 fn check_len(&self) {
216 if self.items.len() > u32::MAX as usize {
217 panic!("arena length must fit in u32");
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::{IdRange, Idx, ListArena};
225
226 #[test]
227 fn typed_index_round_trips_raw_values() {
228 let id = Idx::<String>::new(42);
229 assert_eq!(id.index(), 42);
230 assert_eq!(id.raw(), 42);
231 assert_eq!(Idx::<String>::from_raw(7).index(), 7);
232 }
233
234 #[test]
235 #[should_panic(expected = "arena index must fit in u32")]
236 fn typed_index_panics_when_out_of_bounds() {
237 let _ = Idx::<()>::new(u32::MAX as usize + 1);
238 }
239
240 #[test]
241 fn typed_ranges_slice_storage() {
242 let values = [10, 20, 30, 40];
243 let range = IdRange::<i32>::from_start_len(1, 2);
244 assert_eq!(range.start().index(), 1);
245 assert_eq!(range.len(), 2);
246 assert_eq!(range.slice(&values), &[20, 30]);
247 }
248
249 #[test]
250 fn empty_ranges_are_zero_length() {
251 let range = IdRange::<i32>::empty();
252 let values = [10, 20];
253 assert!(range.is_empty());
254 assert_eq!(range.slice(&values), &[]);
255 }
256
257 #[test]
258 #[should_panic(expected = "arena range end must fit in u32")]
259 fn typed_range_panics_when_end_is_out_of_bounds() {
260 let _ = IdRange::<()>::from_start_len(u32::MAX as usize, 1);
261 }
262
263 #[test]
264 fn list_arena_packs_variable_length_lists() {
265 let mut arena = ListArena::new();
266 let first = arena.push_many([1, 2]);
267 let empty = arena.push_many([]);
268 let second = arena.push_many([3, 4, 5]);
269
270 assert_eq!(arena.get(first), &[1, 2]);
271 assert_eq!(arena.get(empty), &[]);
272 assert_eq!(arena.get(second), &[3, 4, 5]);
273 assert_eq!(arena.as_slice(), &[1, 2, 3, 4, 5]);
274 }
275
276 #[test]
277 fn list_arena_mutates_ranges_in_place() {
278 let mut arena = ListArena::new();
279 let range = arena.push_many([1, 2, 3]);
280 arena.get_mut(range)[1] = 9;
281 assert_eq!(arena.get(range), &[1, 9, 3]);
282 }
283}