1818use std:: mem;
1919use std:: num:: NonZeroUsize ;
2020
21- /// A stable index into an [`Arena`] for as long as its slot remains occupied.
22- #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
23- pub struct ArenaKey ( usize ) ;
24-
25- impl ArenaKey {
26- /// Encodes this key so it can provide a niche when stored in an `Option`-wrapped structure.
27- pub fn encode ( self ) -> NonZeroUsize {
21+ /// Identifies a reusable slot while that slot is occupied.
22+ ///
23+ /// The non-zero representation lets wrappers such as `WaiterId` retain a niche when stored in an
24+ /// `Option`. Slot IDs deliberately carry no generation: each consumer supplies the cheaper
25+ /// lifecycle rule that matches its waiter storage.
26+ #[ derive( Clone , Copy , Eq , PartialEq ) ]
27+ pub struct SlotId ( NonZeroUsize ) ;
28+
29+ impl SlotId {
30+ fn from_index ( index : usize ) -> Self {
2831 // `Slot<T>` is non-zero-sized, so a Vec of slots cannot reach `usize::MAX` elements.
29- unsafe { NonZeroUsize :: new_unchecked ( self . 0 + 1 ) }
32+ let encoded = index
33+ . checked_add ( 1 )
34+ . expect ( "arena index must fit in a non-zero usize" ) ;
35+ Self ( NonZeroUsize :: new ( encoded) . expect ( "encoded arena index must be non-zero" ) )
36+ }
37+
38+ fn index ( self ) -> usize {
39+ self . 0 . get ( ) - 1
3040 }
41+ }
3142
32- /// Decodes a key produced by [`Self::encode`].
33- pub fn decode ( encoded : NonZeroUsize ) -> Self {
34- Self ( encoded . get ( ) - 1 )
43+ impl std :: fmt :: Debug for SlotId {
44+ fn fmt ( & self , f : & mut std :: fmt :: Formatter < ' _ > ) -> std :: fmt :: Result {
45+ f . debug_tuple ( "SlotId" ) . field ( & self . index ( ) ) . finish ( )
3546 }
3647}
3748
3849/// Minimal reusable storage for internal waiter state.
50+ ///
51+ /// The occupied length equals the number of `Occupied` slots. Every `Vacant` slot appears exactly
52+ /// once in the singly linked vacant list, which starts at `next_vacant` and terminates at
53+ /// `slots.len()`. Removing a value makes its slot ID available for immediate reuse.
3954#[ derive( Debug ) ]
4055pub struct Arena < T > {
4156 slots : Vec < Slot < T > > ,
@@ -45,8 +60,12 @@ pub struct Arena<T> {
4560}
4661
4762/// Values extracted from an [`Arena`], storing the common single-value case inline.
63+ ///
64+ /// This is the specialized subset of a small-vector abstraction needed here: keep one value inline,
65+ /// store additional values in a `Vec`, and support consuming iteration. Keeping that representation
66+ /// focused avoids a general unsafe collection implementation for a single internal operation.
4867#[ derive( Debug ) ]
49- pub struct ArenaValues < T > {
68+ struct ArenaValues < T > {
5069 first : Option < T > ,
5170 rest : Vec < T > ,
5271}
@@ -63,7 +82,7 @@ impl<T> IntoIterator for ArenaValues<T> {
6382#[ derive( Debug ) ]
6483enum Slot < T > {
6584 Occupied ( T ) ,
66- Vacant ( usize ) ,
85+ Vacant { next : usize } ,
6786}
6887
6988impl < T > Arena < T > {
@@ -83,61 +102,76 @@ impl<T> Arena<T> {
83102 }
84103 }
85104
86- pub fn insert ( & mut self , value : T ) -> ArenaKey {
87- let key = self . next_vacant ;
105+ pub fn insert ( & mut self , value : T ) -> SlotId {
106+ let index = self . next_vacant ;
88107 self . len += 1 ;
89108
90- if key == self . slots . len ( ) {
109+ if index == self . slots . len ( ) {
91110 self . slots . push ( Slot :: Occupied ( value) ) ;
92- self . next_vacant = key + 1 ;
111+ self . next_vacant = index + 1 ;
93112 } else {
94- self . next_vacant = match self . slots . get ( key ) {
95- Some ( Slot :: Vacant ( next) ) => * next,
113+ self . next_vacant = match self . slots . get ( index ) {
114+ Some ( Slot :: Vacant { next } ) => * next,
96115 Some ( Slot :: Occupied ( _) ) | None => {
97116 unreachable ! ( "arena free list must point to a vacant slot" )
98117 }
99118 } ;
100- self . slots [ key ] = Slot :: Occupied ( value) ;
119+ self . slots [ index ] = Slot :: Occupied ( value) ;
101120 }
102121
103- ArenaKey ( key )
122+ SlotId :: from_index ( index )
104123 }
105124
106- pub fn get ( & self , key : ArenaKey ) -> Option < & T > {
107- match self . slots . get ( key . 0 ) {
125+ pub fn get ( & self , id : SlotId ) -> Option < & T > {
126+ match self . slots . get ( id . index ( ) ) {
108127 Some ( Slot :: Occupied ( value) ) => Some ( value) ,
109- Some ( Slot :: Vacant ( _ ) ) | None => None ,
128+ Some ( Slot :: Vacant { .. } ) | None => None ,
110129 }
111130 }
112131
113- pub fn get_mut ( & mut self , key : ArenaKey ) -> Option < & mut T > {
114- match self . slots . get_mut ( key . 0 ) {
132+ pub fn get_mut ( & mut self , id : SlotId ) -> Option < & mut T > {
133+ match self . slots . get_mut ( id . index ( ) ) {
115134 Some ( Slot :: Occupied ( value) ) => Some ( value) ,
116- Some ( Slot :: Vacant ( _ ) ) | None => None ,
135+ Some ( Slot :: Vacant { .. } ) | None => None ,
117136 }
118137 }
119138
120- pub fn remove ( & mut self , key : ArenaKey ) -> T {
121- let index = key. 0 ;
139+ /// Removes the value stored at `id`.
140+ ///
141+ /// # Panics
142+ ///
143+ /// Panics if the slot ID is out of bounds or its slot is already vacant. Either case is an
144+ /// internal waiter-lifecycle violation rather than a recoverable lookup failure.
145+ #[ track_caller]
146+ pub fn remove ( & mut self , id : SlotId ) -> T {
147+ let index = id. index ( ) ;
122148 let slot = self
123149 . slots
124150 . get_mut ( index)
125- . expect ( "arena key must be in bounds" ) ;
126- let value = match mem:: replace ( slot, Slot :: Vacant ( self . next_vacant ) ) {
151+ . expect ( "arena slot ID must be in bounds" ) ;
152+ let value = match mem:: replace (
153+ slot,
154+ Slot :: Vacant {
155+ next : self . next_vacant ,
156+ } ,
157+ ) {
127158 Slot :: Occupied ( value) => value,
128- vacant @ Slot :: Vacant ( _ ) => {
159+ vacant @ Slot :: Vacant { .. } => {
129160 * slot = vacant;
130- panic ! ( "arena key must be occupied" ) ;
161+ panic ! ( "arena slot ID must be occupied" ) ;
131162 }
132163 } ;
133164 self . len -= 1 ;
134165 self . next_vacant = index;
135166 value
136167 }
137168
138- /// Takes every occupied value while retaining the allocation for reuse.
169+ /// Takes every occupied value in slot order while retaining the allocation for reuse.
170+ ///
171+ /// Every previously issued slot ID becomes invalid, including IDs for slots that were already
172+ /// vacant. Consumers that retain IDs across this operation must supply their own epoch check.
139173 #[ inline]
140- pub fn take_all ( & mut self ) -> ArenaValues < T > {
174+ pub fn take_all ( & mut self ) -> impl Iterator < Item = T > + use < T > {
141175 let len = self . len ;
142176 let mut values = ArenaValues {
143177 first : None ,
@@ -158,7 +192,7 @@ impl<T> Arena<T> {
158192
159193 self . next_vacant = 0 ;
160194 self . len = 0 ;
161- values
195+ values. into_iter ( )
162196 }
163197
164198 #[ cfg( test) ]
@@ -171,6 +205,11 @@ impl<T> Arena<T> {
171205mod tests {
172206 use super :: * ;
173207
208+ #[ test]
209+ fn slot_id_preserves_the_option_niche ( ) {
210+ assert_eq ! ( size_of:: <SlotId >( ) , size_of:: <Option <SlotId >>( ) ) ;
211+ }
212+
174213 #[ test]
175214 fn removed_slots_are_reused ( ) {
176215 let mut arena = Arena :: new ( ) ;
@@ -186,19 +225,19 @@ mod tests {
186225 }
187226
188227 #[ test]
189- fn take_all_restarts_key_allocation ( ) {
228+ fn take_all_restarts_slot_id_allocation ( ) {
190229 let mut arena = Arena :: with_capacity ( 3 ) ;
191230 let first = arena. insert ( 1 ) ;
192231 let second = arena. insert ( 2 ) ;
193232 let third = arena. insert ( 3 ) ;
194233 let capacity = arena. slots . capacity ( ) ;
195234 arena. remove ( second) ;
196235
197- assert_eq ! ( arena. take_all( ) . into_iter ( ) . collect:: <Vec <_>>( ) , vec![ 1 , 3 ] ) ;
236+ assert_eq ! ( arena. take_all( ) . collect:: <Vec <_>>( ) , vec![ 1 , 3 ] ) ;
198237 assert_eq ! ( arena. len( ) , 0 ) ;
199238 assert_eq ! ( arena. slots. capacity( ) , capacity) ;
200239
201- let keys = [ arena. insert ( 4 ) , arena. insert ( 5 ) , arena. insert ( 6 ) ] ;
202- assert_eq ! ( keys , [ first, second, third] ) ;
240+ let slot_ids = [ arena. insert ( 4 ) , arena. insert ( 5 ) , arena. insert ( 6 ) ] ;
241+ assert_eq ! ( slot_ids , [ first, second, third] ) ;
203242 }
204243}
0 commit comments