Skip to main content

goud_engine/ecs/
entity.rs

1//! Entity identifiers for the ECS.
2//!
3//! Entities are lightweight identifiers that serve as keys for component data.
4//! Unlike [`Handle<T>`](crate::core::handle::Handle), entities are not generic -
5//! they identify an entity across all component storages rather than a specific
6//! resource type.
7//!
8//! # Design Pattern: Generational Indices
9//!
10//! Entities use the same generational index pattern as handles:
11//!
12//! 1. Each entity has an index (slot in the entity array) and a generation
13//! 2. When an entity is despawned, its generation increments
14//! 3. Old entity references become stale (generation mismatch)
15//! 4. The slot can be reused for new entities with the new generation
16//!
17//! This prevents "dangling entity" bugs where code holds a reference to a
18//! despawned entity and accidentally accesses data from a new entity that
19//! reused the same slot.
20//!
21//! # Example
22//!
23//! ```
24//! use goud_engine::ecs::entity::Entity;
25//!
26//! // Entities are typically created by EntityAllocator, but can be constructed directly
27//! let entity = Entity::new(0, 1);
28//!
29//! assert_eq!(entity.index(), 0);
30//! assert_eq!(entity.generation(), 1);
31//!
32//! // PLACEHOLDER is a special sentinel value
33//! assert!(Entity::PLACEHOLDER.is_placeholder());
34//! ```
35//!
36//! # FFI Safety
37//!
38//! Entity uses `#[repr(C)]` for predictable memory layout across FFI boundaries.
39//! The struct is exactly 8 bytes: 4 bytes for index + 4 bytes for generation.
40
41use std::fmt;
42use std::hash::{Hash, Hasher};
43
44// =============================================================================
45// EntityAllocator
46// =============================================================================
47
48/// Manages entity ID allocation with generation counting and free-list recycling.
49///
50/// The `EntityAllocator` is responsible for creating and invalidating entities
51/// in a memory-efficient manner. It uses a generational index scheme where:
52///
53/// - Each slot has a generation counter that increments on deallocation
54/// - Deallocated slots are recycled via a free list
55/// - Entities carry their generation, allowing stale entity detection
56///
57/// # Design Pattern: Generational Arena Allocator
58///
59/// This pattern provides:
60/// - O(1) allocation (pop from free list or push new entry)
61/// - O(1) deallocation (push to free list, increment generation)
62/// - O(1) liveness check (compare generations)
63/// - Memory reuse without use-after-free bugs
64///
65/// # Difference from HandleAllocator
66///
67/// Unlike [`HandleAllocator<T>`](crate::core::handle::HandleAllocator), which is
68/// generic over a marker type for type safety, `EntityAllocator` is specifically
69/// designed for ECS entities and produces non-generic [`Entity`] values.
70///
71/// # Example
72///
73/// ```
74/// use goud_engine::ecs::entity::EntityAllocator;
75///
76/// let mut allocator = EntityAllocator::new();
77///
78/// // Allocate some entities
79/// let e1 = allocator.allocate();
80/// let e2 = allocator.allocate();
81///
82/// assert!(allocator.is_alive(e1));
83/// assert!(allocator.is_alive(e2));
84///
85/// // Deallocate e1
86/// assert!(allocator.deallocate(e1));
87/// assert!(!allocator.is_alive(e1));  // e1 is now stale
88///
89/// // Allocate again - may reuse e1's slot with new generation
90/// let e3 = allocator.allocate();
91/// assert!(allocator.is_alive(e3));
92///
93/// // e1 still references old generation, so it's still not alive
94/// assert!(!allocator.is_alive(e1));
95/// ```
96///
97/// # Thread Safety
98///
99/// `EntityAllocator` is NOT thread-safe. For concurrent access, wrap in
100/// appropriate synchronization primitives (Mutex, RwLock, etc.).
101pub struct EntityAllocator {
102    /// Generation counter for each slot.
103    ///
104    /// Index `i` stores the current generation for slot `i`.
105    /// Generation starts at 1 for new slots (0 is reserved for never-allocated).
106    generations: Vec<u32>,
107
108    /// Stack of free slot indices available for reuse.
109    ///
110    /// When an entity is deallocated, its index is pushed here.
111    /// On allocation, we prefer to pop from this list before growing.
112    free_list: Vec<u32>,
113}
114
115impl EntityAllocator {
116    /// Creates a new, empty entity allocator.
117    ///
118    /// The allocator starts with no pre-allocated capacity.
119    /// Use [`with_capacity`](Self::with_capacity) for bulk allocation scenarios.
120    ///
121    /// # Example
122    ///
123    /// ```
124    /// use goud_engine::ecs::entity::EntityAllocator;
125    ///
126    /// let allocator = EntityAllocator::new();
127    /// assert_eq!(allocator.len(), 0);
128    /// assert!(allocator.is_empty());
129    /// ```
130    #[inline]
131    pub fn new() -> Self {
132        Self {
133            generations: Vec::new(),
134            free_list: Vec::new(),
135        }
136    }
137
138    /// Creates a new entity allocator with pre-allocated capacity.
139    ///
140    /// This is useful when you know approximately how many entities you'll need,
141    /// as it avoids repeated reallocations during bulk allocation.
142    ///
143    /// Note that this only pre-allocates memory for the internal vectors;
144    /// it does not create any entities. Use [`allocate`](Self::allocate) to
145    /// actually create entities.
146    ///
147    /// # Arguments
148    ///
149    /// * `capacity` - The number of slots to pre-allocate
150    ///
151    /// # Example
152    ///
153    /// ```
154    /// use goud_engine::ecs::entity::EntityAllocator;
155    ///
156    /// // Pre-allocate space for 1000 entities
157    /// let mut allocator = EntityAllocator::with_capacity(1000);
158    ///
159    /// // No entities allocated yet, but memory is reserved
160    /// assert_eq!(allocator.len(), 0);
161    /// assert!(allocator.is_empty());
162    ///
163    /// // Allocations up to 1000 won't cause reallocation
164    /// for _ in 0..1000 {
165    ///     allocator.allocate();
166    /// }
167    /// assert_eq!(allocator.len(), 1000);
168    /// ```
169    #[inline]
170    pub fn with_capacity(capacity: usize) -> Self {
171        Self {
172            generations: Vec::with_capacity(capacity),
173            free_list: Vec::new(), // Free list starts empty, no freed slots yet
174        }
175    }
176
177    /// Allocates a new entity.
178    ///
179    /// If there are slots in the free list, one is reused with an incremented
180    /// generation. Otherwise, a new slot is created with generation 1.
181    ///
182    /// # Returns
183    ///
184    /// A new, valid entity that can be used in ECS operations.
185    ///
186    /// # Panics
187    ///
188    /// Panics if the number of slots exceeds `u32::MAX - 1` (unlikely in practice,
189    /// as this would require over 4 billion entity allocations without reuse).
190    ///
191    /// # Example
192    ///
193    /// ```
194    /// use goud_engine::ecs::entity::EntityAllocator;
195    ///
196    /// let mut allocator = EntityAllocator::new();
197    ///
198    /// let e1 = allocator.allocate();
199    /// let e2 = allocator.allocate();
200    ///
201    /// assert_ne!(e1, e2);
202    /// assert!(!e1.is_placeholder());
203    /// assert!(!e2.is_placeholder());
204    /// ```
205    pub fn allocate(&mut self) -> Entity {
206        if let Some(index) = self.free_list.pop() {
207            // Reuse a slot from the free list
208            // Generation was already incremented during deallocation
209            let generation = self.generations[index as usize];
210            Entity::new(index, generation)
211        } else {
212            // Allocate a new slot
213            let index = self.generations.len();
214
215            // Ensure we don't exceed u32::MAX - 1 (reserve MAX for PLACEHOLDER)
216            assert!(
217                index < u32::MAX as usize,
218                "EntityAllocator exceeded maximum capacity"
219            );
220
221            // New slots start at generation 1 (0 is reserved for never-allocated/PLACEHOLDER)
222            self.generations.push(1);
223
224            Entity::new(index as u32, 1)
225        }
226    }
227
228    /// Deallocates an entity, making it invalid.
229    ///
230    /// The slot's generation is incremented, invalidating any entity references
231    /// that use the old generation. The slot is added to the free list for
232    /// future reuse.
233    ///
234    /// # Arguments
235    ///
236    /// * `entity` - The entity to deallocate
237    ///
238    /// # Returns
239    ///
240    /// - `true` if the entity was valid and successfully deallocated
241    /// - `false` if the entity was invalid (wrong generation, out of bounds, or PLACEHOLDER)
242    ///
243    /// # Double Deallocation
244    ///
245    /// Attempting to deallocate the same entity twice returns `false` on the
246    /// second attempt, as the generation will have already been incremented.
247    ///
248    /// # Example
249    ///
250    /// ```
251    /// use goud_engine::ecs::entity::EntityAllocator;
252    ///
253    /// let mut allocator = EntityAllocator::new();
254    /// let entity = allocator.allocate();
255    ///
256    /// assert!(allocator.is_alive(entity));
257    /// assert!(allocator.deallocate(entity));  // First deallocation succeeds
258    /// assert!(!allocator.is_alive(entity));
259    /// assert!(!allocator.deallocate(entity)); // Second deallocation fails
260    /// ```
261    pub fn deallocate(&mut self, entity: Entity) -> bool {
262        // PLACEHOLDER entities cannot be deallocated
263        if entity.is_placeholder() {
264            return false;
265        }
266
267        let index = entity.index() as usize;
268
269        // Check bounds
270        if index >= self.generations.len() {
271            return false;
272        }
273
274        // Check generation matches (entity is still alive)
275        if self.generations[index] != entity.generation() {
276            return false;
277        }
278
279        // Increment generation to invalidate existing entity references
280        // Wrap at u32::MAX to 1 (skip 0, which is reserved)
281        let new_gen = self.generations[index].wrapping_add(1);
282        self.generations[index] = if new_gen == 0 { 1 } else { new_gen };
283
284        // Add to free list for reuse
285        self.free_list.push(entity.index());
286
287        true
288    }
289
290    /// Checks if an entity is currently alive.
291    ///
292    /// An entity is "alive" if:
293    /// - It is not the PLACEHOLDER sentinel
294    /// - Its index is within bounds
295    /// - Its generation matches the current slot generation
296    ///
297    /// # Arguments
298    ///
299    /// * `entity` - The entity to check
300    ///
301    /// # Returns
302    ///
303    /// `true` if the entity is alive, `false` otherwise.
304    ///
305    /// # Example
306    ///
307    /// ```
308    /// use goud_engine::ecs::entity::EntityAllocator;
309    ///
310    /// let mut allocator = EntityAllocator::new();
311    /// let entity = allocator.allocate();
312    ///
313    /// assert!(allocator.is_alive(entity));
314    ///
315    /// allocator.deallocate(entity);
316    /// assert!(!allocator.is_alive(entity));
317    /// ```
318    #[inline]
319    pub fn is_alive(&self, entity: Entity) -> bool {
320        // PLACEHOLDER entities are never alive
321        if entity.is_placeholder() {
322            return false;
323        }
324
325        let index = entity.index() as usize;
326
327        // Check bounds and generation
328        index < self.generations.len() && self.generations[index] == entity.generation()
329    }
330
331    /// Returns the number of currently allocated (alive) entities.
332    ///
333    /// This is the total capacity minus the number of free slots.
334    ///
335    /// # Example
336    ///
337    /// ```
338    /// use goud_engine::ecs::entity::EntityAllocator;
339    ///
340    /// let mut allocator = EntityAllocator::new();
341    /// assert_eq!(allocator.len(), 0);
342    ///
343    /// let e1 = allocator.allocate();
344    /// let e2 = allocator.allocate();
345    /// assert_eq!(allocator.len(), 2);
346    ///
347    /// allocator.deallocate(e1);
348    /// assert_eq!(allocator.len(), 1);
349    /// ```
350    #[inline]
351    pub fn len(&self) -> usize {
352        self.generations.len() - self.free_list.len()
353    }
354
355    /// Returns the total number of slots (both allocated and free).
356    ///
357    /// This represents the high-water mark of allocations.
358    ///
359    /// # Example
360    ///
361    /// ```
362    /// use goud_engine::ecs::entity::EntityAllocator;
363    ///
364    /// let mut allocator = EntityAllocator::new();
365    /// let e1 = allocator.allocate();
366    /// let e2 = allocator.allocate();
367    ///
368    /// assert_eq!(allocator.capacity(), 2);
369    ///
370    /// allocator.deallocate(e1);
371    /// // Capacity remains 2, even though one slot is free
372    /// assert_eq!(allocator.capacity(), 2);
373    /// ```
374    #[inline]
375    pub fn capacity(&self) -> usize {
376        self.generations.len()
377    }
378
379    /// Returns `true` if no entities are currently allocated.
380    ///
381    /// # Example
382    ///
383    /// ```
384    /// use goud_engine::ecs::entity::EntityAllocator;
385    ///
386    /// let mut allocator = EntityAllocator::new();
387    /// assert!(allocator.is_empty());
388    ///
389    /// let entity = allocator.allocate();
390    /// assert!(!allocator.is_empty());
391    ///
392    /// allocator.deallocate(entity);
393    /// assert!(allocator.is_empty());
394    /// ```
395    #[inline]
396    pub fn is_empty(&self) -> bool {
397        self.len() == 0
398    }
399
400    // =========================================================================
401    // Bulk Operations
402    // =========================================================================
403
404    /// Allocates multiple entities at once.
405    ///
406    /// This is more efficient than calling [`allocate`](Self::allocate) in a loop
407    /// because it pre-allocates the result vector and minimizes reallocations.
408    ///
409    /// # Arguments
410    ///
411    /// * `count` - The number of entities to allocate
412    ///
413    /// # Returns
414    ///
415    /// A vector containing `count` newly allocated entities. All entities are
416    /// guaranteed to be valid and unique.
417    ///
418    /// # Panics
419    ///
420    /// Panics if allocating `count` entities would exceed `u32::MAX - 1` total slots.
421    ///
422    /// # Performance
423    ///
424    /// For large batch allocations, this method:
425    /// - Pre-allocates the result vector with exact capacity
426    /// - Reuses free slots first (LIFO order)
427    /// - Bulk-extends the generations vector for remaining slots
428    ///
429    /// # Example
430    ///
431    /// ```
432    /// use goud_engine::ecs::entity::EntityAllocator;
433    ///
434    /// let mut allocator = EntityAllocator::new();
435    ///
436    /// // Allocate 1000 entities in one call
437    /// let entities = allocator.allocate_batch(1000);
438    /// assert_eq!(entities.len(), 1000);
439    /// assert_eq!(allocator.len(), 1000);
440    ///
441    /// // All entities are unique and alive
442    /// for entity in &entities {
443    ///     assert!(allocator.is_alive(*entity));
444    /// }
445    /// ```
446    pub fn allocate_batch(&mut self, count: usize) -> Vec<Entity> {
447        if count == 0 {
448            return Vec::new();
449        }
450
451        let mut entities = Vec::with_capacity(count);
452
453        // First, use up free slots
454        let from_free_list = count.min(self.free_list.len());
455        for _ in 0..from_free_list {
456            if let Some(index) = self.free_list.pop() {
457                let generation = self.generations[index as usize];
458                entities.push(Entity::new(index, generation));
459            }
460        }
461
462        // Then, allocate new slots for remaining count
463        let remaining = count - from_free_list;
464        if remaining > 0 {
465            let start_index = self.generations.len();
466            let end_index = start_index + remaining;
467
468            // Ensure we don't exceed maximum capacity
469            assert!(
470                end_index <= u32::MAX as usize,
471                "EntityAllocator would exceed maximum capacity"
472            );
473
474            // Bulk-extend generations vector with initial generation of 1
475            self.generations.resize(end_index, 1);
476
477            // Create entities for new slots
478            for index in start_index..end_index {
479                entities.push(Entity::new(index as u32, 1));
480            }
481        }
482
483        entities
484    }
485
486    /// Deallocates multiple entities at once.
487    ///
488    /// This method attempts to deallocate each entity in the slice. Invalid
489    /// entities (already deallocated, wrong generation, out of bounds, or
490    /// PLACEHOLDER) are skipped without error.
491    ///
492    /// # Arguments
493    ///
494    /// * `entities` - A slice of entities to deallocate
495    ///
496    /// # Returns
497    ///
498    /// The number of entities that were successfully deallocated.
499    ///
500    /// # Example
501    ///
502    /// ```
503    /// use goud_engine::ecs::entity::EntityAllocator;
504    ///
505    /// let mut allocator = EntityAllocator::new();
506    /// let entities = allocator.allocate_batch(100);
507    /// assert_eq!(allocator.len(), 100);
508    ///
509    /// // Deallocate all at once
510    /// let deallocated = allocator.deallocate_batch(&entities);
511    /// assert_eq!(deallocated, 100);
512    /// assert_eq!(allocator.len(), 0);
513    ///
514    /// // Second deallocation returns 0 (already dead)
515    /// let deallocated_again = allocator.deallocate_batch(&entities);
516    /// assert_eq!(deallocated_again, 0);
517    /// ```
518    pub fn deallocate_batch(&mut self, entities: &[Entity]) -> usize {
519        let mut success_count = 0;
520
521        for entity in entities {
522            if self.deallocate(*entity) {
523                success_count += 1;
524            }
525        }
526
527        success_count
528    }
529
530    /// Reserves capacity for at least `additional` more entities.
531    ///
532    /// This pre-allocates memory in the internal generations vector, avoiding
533    /// reallocations during subsequent allocations. Use this when you know
534    /// approximately how many entities you'll need.
535    ///
536    /// Note that this only affects the generations vector capacity. It does not
537    /// create any entities or affect the free list.
538    ///
539    /// # Arguments
540    ///
541    /// * `additional` - The number of additional slots to reserve
542    ///
543    /// # Example
544    ///
545    /// ```
546    /// use goud_engine::ecs::entity::EntityAllocator;
547    ///
548    /// let mut allocator = EntityAllocator::new();
549    ///
550    /// // Allocate some entities
551    /// let initial = allocator.allocate_batch(100);
552    /// assert_eq!(allocator.len(), 100);
553    ///
554    /// // Reserve space for 1000 more
555    /// allocator.reserve(1000);
556    ///
557    /// // Now allocations up to capacity won't cause reallocation
558    /// let more = allocator.allocate_batch(1000);
559    /// assert_eq!(allocator.len(), 1100);
560    /// ```
561    #[inline]
562    pub fn reserve(&mut self, additional: usize) {
563        self.generations.reserve(additional);
564    }
565}
566
567impl Default for EntityAllocator {
568    /// Creates an empty entity allocator.
569    ///
570    /// Equivalent to [`EntityAllocator::new()`].
571    #[inline]
572    fn default() -> Self {
573        Self::new()
574    }
575}
576
577impl fmt::Debug for EntityAllocator {
578    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579        f.debug_struct("EntityAllocator")
580            .field("len", &self.len())
581            .field("capacity", &self.capacity())
582            .field("free_slots", &self.free_list.len())
583            .finish()
584    }
585}
586
587// =============================================================================
588// Entity
589// =============================================================================
590
591/// A lightweight identifier for an entity in the ECS.
592///
593/// Entities are the "E" in ECS - they are identifiers that components attach to.
594/// An entity by itself has no data; it's purely an ID used to look up components.
595///
596/// # Memory Layout
597///
598/// ```text
599/// Entity (8 bytes total):
600/// ┌────────────────┬────────────────┐
601/// │  index (u32)   │ generation(u32)│
602/// └────────────────┴────────────────┘
603/// ```
604///
605/// # Thread Safety
606///
607/// Entity is `Copy`, `Clone`, `Send`, and `Sync`. Entity values can be freely
608/// shared across threads. However, operations on the ECS world that use entities
609/// require appropriate synchronization.
610#[repr(C)]
611#[derive(Clone, Copy, PartialEq, Eq)]
612pub struct Entity {
613    /// The index of this entity in the entity array.
614    ///
615    /// This corresponds to a slot in the entity allocator.
616    index: u32,
617
618    /// The generation of this entity.
619    ///
620    /// Incremented each time a slot is reused, allowing detection of stale
621    /// entity references.
622    generation: u32,
623}
624
625impl Entity {
626    /// A placeholder entity that should never be returned by the allocator.
627    ///
628    /// Use this as a sentinel value for "no entity" or uninitialized entity fields.
629    /// The placeholder uses `u32::MAX` for the index, which the allocator will never
630    /// return (it would require 4 billion+ entity allocations first).
631    ///
632    /// # Example
633    ///
634    /// ```
635    /// use goud_engine::ecs::entity::Entity;
636    ///
637    /// let mut maybe_entity = Entity::PLACEHOLDER;
638    /// assert!(maybe_entity.is_placeholder());
639    ///
640    /// // Later, assign a real entity
641    /// maybe_entity = Entity::new(0, 1);
642    /// assert!(!maybe_entity.is_placeholder());
643    /// ```
644    pub const PLACEHOLDER: Entity = Entity {
645        index: u32::MAX,
646        generation: 0,
647    };
648
649    /// Creates a new entity with the given index and generation.
650    ///
651    /// This is primarily used by [`EntityAllocator`]. Direct construction is
652    /// possible but not recommended for typical use.
653    ///
654    /// # Arguments
655    ///
656    /// * `index` - The slot index for this entity
657    /// * `generation` - The generation counter for this slot
658    ///
659    /// # Example
660    ///
661    /// ```
662    /// use goud_engine::ecs::entity::Entity;
663    ///
664    /// let entity = Entity::new(42, 1);
665    /// assert_eq!(entity.index(), 42);
666    /// assert_eq!(entity.generation(), 1);
667    /// ```
668    #[inline]
669    pub const fn new(index: u32, generation: u32) -> Self {
670        Self { index, generation }
671    }
672
673    /// Returns the index of this entity.
674    ///
675    /// The index is the slot in the entity allocator's internal array.
676    /// Multiple entities may share the same index (at different times),
677    /// distinguished by their generation.
678    #[inline]
679    pub const fn index(&self) -> u32 {
680        self.index
681    }
682
683    /// Returns the generation of this entity.
684    ///
685    /// The generation increments each time a slot is reused. Comparing generations
686    /// allows detecting stale entity references.
687    #[inline]
688    pub const fn generation(&self) -> u32 {
689        self.generation
690    }
691
692    /// Returns `true` if this is the placeholder entity.
693    ///
694    /// The placeholder entity should never be used in actual ECS operations.
695    /// It's a sentinel value for "no entity" situations.
696    #[inline]
697    pub const fn is_placeholder(&self) -> bool {
698        self.index == u32::MAX && self.generation == 0
699    }
700
701    /// Packs the entity into a single `u64` value.
702    ///
703    /// Format: upper 32 bits = generation, lower 32 bits = index.
704    ///
705    /// This is useful for FFI or when a single integer representation is needed.
706    ///
707    /// # Example
708    ///
709    /// ```
710    /// use goud_engine::ecs::entity::Entity;
711    ///
712    /// let entity = Entity::new(42, 7);
713    /// let packed = entity.to_bits();
714    ///
715    /// // Upper 32 bits: generation (7), Lower 32 bits: index (42)
716    /// assert_eq!(packed, (7_u64 << 32) | 42);
717    ///
718    /// // Round-trip
719    /// let unpacked = Entity::from_bits(packed);
720    /// assert_eq!(entity, unpacked);
721    /// ```
722    #[inline]
723    pub const fn to_bits(&self) -> u64 {
724        ((self.generation as u64) << 32) | (self.index as u64)
725    }
726
727    /// Creates an entity from a packed `u64` value.
728    ///
729    /// Format: upper 32 bits = generation, lower 32 bits = index.
730    ///
731    /// # Example
732    ///
733    /// ```
734    /// use goud_engine::ecs::entity::Entity;
735    ///
736    /// let packed = (3_u64 << 32) | 100;
737    /// let entity = Entity::from_bits(packed);
738    ///
739    /// assert_eq!(entity.index(), 100);
740    /// assert_eq!(entity.generation(), 3);
741    /// ```
742    #[inline]
743    pub const fn from_bits(bits: u64) -> Self {
744        Self {
745            index: bits as u32,
746            generation: (bits >> 32) as u32,
747        }
748    }
749}
750
751// =============================================================================
752// Trait Implementations
753// =============================================================================
754
755impl Hash for Entity {
756    /// Hashes the entity by combining index and generation.
757    ///
758    /// Two entities with the same index but different generations will hash
759    /// differently, which is important for using entities as map keys.
760    #[inline]
761    fn hash<H: Hasher>(&self, state: &mut H) {
762        // Hash as u64 for efficiency (single hash operation)
763        self.to_bits().hash(state);
764    }
765}
766
767impl fmt::Debug for Entity {
768    /// Formats the entity as `Entity(index:generation)`.
769    ///
770    /// # Example
771    ///
772    /// ```
773    /// use goud_engine::ecs::entity::Entity;
774    ///
775    /// let entity = Entity::new(42, 3);
776    /// assert_eq!(format!("{:?}", entity), "Entity(42:3)");
777    /// ```
778    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
779        write!(f, "Entity({}:{})", self.index, self.generation)
780    }
781}
782
783impl fmt::Display for Entity {
784    /// Formats the entity in a user-friendly way.
785    ///
786    /// Same format as Debug: `Entity(index:generation)`.
787    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
788        write!(f, "Entity({}:{})", self.index, self.generation)
789    }
790}
791
792impl Default for Entity {
793    /// Returns the placeholder entity.
794    ///
795    /// Using Default returns PLACEHOLDER, which should be treated as "no entity".
796    #[inline]
797    fn default() -> Self {
798        Self::PLACEHOLDER
799    }
800}
801
802impl From<Entity> for u64 {
803    /// Converts an entity to its packed `u64` representation.
804    #[inline]
805    fn from(entity: Entity) -> Self {
806        entity.to_bits()
807    }
808}
809
810impl From<u64> for Entity {
811    /// Creates an entity from a packed `u64` representation.
812    #[inline]
813    fn from(bits: u64) -> Self {
814        Entity::from_bits(bits)
815    }
816}
817
818// =============================================================================
819// Unit Tests
820// =============================================================================
821
822#[cfg(test)]
823mod tests {
824    use super::*;
825    use std::collections::HashSet;
826
827    // -------------------------------------------------------------------------
828    // Structure Tests
829    // -------------------------------------------------------------------------
830
831    #[test]
832    fn test_entity_new() {
833        let entity = Entity::new(42, 7);
834        assert_eq!(entity.index(), 42);
835        assert_eq!(entity.generation(), 7);
836    }
837
838    #[test]
839    fn test_entity_placeholder() {
840        let placeholder = Entity::PLACEHOLDER;
841        assert_eq!(placeholder.index(), u32::MAX);
842        assert_eq!(placeholder.generation(), 0);
843        assert!(placeholder.is_placeholder());
844
845        // Non-placeholder entities
846        let entity = Entity::new(0, 0);
847        assert!(!entity.is_placeholder());
848
849        let entity = Entity::new(u32::MAX, 1);
850        assert!(!entity.is_placeholder());
851
852        let entity = Entity::new(0, 1);
853        assert!(!entity.is_placeholder());
854    }
855
856    #[test]
857    fn test_entity_size() {
858        // Entity should be exactly 8 bytes (2 x u32)
859        assert_eq!(std::mem::size_of::<Entity>(), 8);
860        assert_eq!(std::mem::align_of::<Entity>(), 4);
861    }
862
863    #[test]
864    fn test_entity_copy_clone() {
865        let entity1 = Entity::new(10, 5);
866        let entity2 = entity1; // Copy
867        let entity3 = entity1.clone(); // Clone
868
869        assert_eq!(entity1, entity2);
870        assert_eq!(entity1, entity3);
871    }
872
873    #[test]
874    fn test_entity_equality() {
875        let e1 = Entity::new(0, 1);
876        let e2 = Entity::new(0, 1);
877        let e3 = Entity::new(0, 2); // Different generation
878        let e4 = Entity::new(1, 1); // Different index
879
880        assert_eq!(e1, e2);
881        assert_ne!(e1, e3);
882        assert_ne!(e1, e4);
883    }
884
885    // -------------------------------------------------------------------------
886    // Bit Packing Tests
887    // -------------------------------------------------------------------------
888
889    #[test]
890    fn test_entity_to_bits() {
891        let entity = Entity::new(42, 7);
892        let bits = entity.to_bits();
893
894        // Upper 32 bits: generation (7), Lower 32 bits: index (42)
895        assert_eq!(bits, (7_u64 << 32) | 42);
896    }
897
898    #[test]
899    fn test_entity_from_bits() {
900        let bits = (3_u64 << 32) | 100;
901        let entity = Entity::from_bits(bits);
902
903        assert_eq!(entity.index(), 100);
904        assert_eq!(entity.generation(), 3);
905    }
906
907    #[test]
908    fn test_entity_bits_roundtrip() {
909        let original = Entity::new(999, 42);
910        let bits = original.to_bits();
911        let restored = Entity::from_bits(bits);
912
913        assert_eq!(original, restored);
914    }
915
916    #[test]
917    fn test_entity_bits_edge_cases() {
918        // Maximum values
919        let max = Entity::new(u32::MAX, u32::MAX);
920        assert_eq!(max, Entity::from_bits(max.to_bits()));
921
922        // Zero values
923        let zero = Entity::new(0, 0);
924        assert_eq!(zero, Entity::from_bits(zero.to_bits()));
925
926        // Mixed
927        let mixed = Entity::new(u32::MAX, 0);
928        assert_eq!(mixed, Entity::from_bits(mixed.to_bits()));
929    }
930
931    // -------------------------------------------------------------------------
932    // Trait Implementation Tests
933    // -------------------------------------------------------------------------
934
935    #[test]
936    fn test_entity_hash() {
937        let mut set = HashSet::new();
938
939        let e1 = Entity::new(0, 1);
940        let e2 = Entity::new(0, 1);
941        let e3 = Entity::new(0, 2);
942        let e4 = Entity::new(1, 1);
943
944        set.insert(e1);
945
946        // Same entity should be found
947        assert!(set.contains(&e2));
948
949        // Different entities should not be found
950        assert!(!set.contains(&e3));
951        assert!(!set.contains(&e4));
952    }
953
954    #[test]
955    fn test_entity_debug_format() {
956        let entity = Entity::new(42, 3);
957        assert_eq!(format!("{:?}", entity), "Entity(42:3)");
958
959        let placeholder = Entity::PLACEHOLDER;
960        assert_eq!(format!("{:?}", placeholder), "Entity(4294967295:0)");
961    }
962
963    #[test]
964    fn test_entity_display_format() {
965        let entity = Entity::new(100, 7);
966        assert_eq!(format!("{}", entity), "Entity(100:7)");
967    }
968
969    #[test]
970    fn test_entity_default() {
971        let default_entity: Entity = Default::default();
972        assert_eq!(default_entity, Entity::PLACEHOLDER);
973        assert!(default_entity.is_placeholder());
974    }
975
976    // -------------------------------------------------------------------------
977    // Conversion Tests
978    // -------------------------------------------------------------------------
979
980    #[test]
981    fn test_entity_from_into_u64() {
982        let entity = Entity::new(42, 7);
983
984        // Into<u64>
985        let bits: u64 = entity.into();
986        assert_eq!(bits, entity.to_bits());
987
988        // From<u64>
989        let restored: Entity = bits.into();
990        assert_eq!(restored, entity);
991    }
992
993    // -------------------------------------------------------------------------
994    // Thread Safety Tests
995    // -------------------------------------------------------------------------
996
997    #[test]
998    fn test_entity_send_sync() {
999        // Compile-time check that Entity is Send + Sync
1000        fn assert_send_sync<T: Send + Sync>() {}
1001        assert_send_sync::<Entity>();
1002    }
1003
1004    // =========================================================================
1005    // EntityAllocator Tests
1006    // =========================================================================
1007
1008    // -------------------------------------------------------------------------
1009    // Basic Allocator Operations
1010    // -------------------------------------------------------------------------
1011
1012    #[test]
1013    fn test_allocator_new() {
1014        let allocator = EntityAllocator::new();
1015        assert_eq!(allocator.len(), 0);
1016        assert_eq!(allocator.capacity(), 0);
1017        assert!(allocator.is_empty());
1018    }
1019
1020    #[test]
1021    fn test_allocator_with_capacity() {
1022        let allocator = EntityAllocator::with_capacity(100);
1023        assert_eq!(allocator.len(), 0);
1024        assert_eq!(allocator.capacity(), 0); // capacity() returns slots used, not reserved
1025        assert!(allocator.is_empty());
1026    }
1027
1028    #[test]
1029    fn test_allocator_default() {
1030        let allocator: EntityAllocator = Default::default();
1031        assert_eq!(allocator.len(), 0);
1032        assert!(allocator.is_empty());
1033    }
1034
1035    #[test]
1036    fn test_allocator_debug() {
1037        let mut allocator = EntityAllocator::new();
1038        let _e1 = allocator.allocate();
1039        let e2 = allocator.allocate();
1040        allocator.deallocate(e2);
1041
1042        let debug_str = format!("{:?}", allocator);
1043        assert!(debug_str.contains("EntityAllocator"));
1044        assert!(debug_str.contains("len"));
1045        assert!(debug_str.contains("capacity"));
1046        assert!(debug_str.contains("free_slots"));
1047    }
1048
1049    // -------------------------------------------------------------------------
1050    // Allocation Tests
1051    // -------------------------------------------------------------------------
1052
1053    #[test]
1054    fn test_allocator_allocate_basic() {
1055        let mut allocator = EntityAllocator::new();
1056
1057        let e1 = allocator.allocate();
1058        assert_eq!(e1.index(), 0);
1059        assert_eq!(e1.generation(), 1);
1060        assert!(!e1.is_placeholder());
1061
1062        let e2 = allocator.allocate();
1063        assert_eq!(e2.index(), 1);
1064        assert_eq!(e2.generation(), 1);
1065
1066        assert_ne!(e1, e2);
1067        assert_eq!(allocator.len(), 2);
1068    }
1069
1070    #[test]
1071    fn test_allocator_allocate_multiple() {
1072        let mut allocator = EntityAllocator::new();
1073        let mut entities = Vec::new();
1074
1075        for _ in 0..100 {
1076            entities.push(allocator.allocate());
1077        }
1078
1079        assert_eq!(allocator.len(), 100);
1080        assert_eq!(allocator.capacity(), 100);
1081
1082        // All entities should be unique
1083        let unique: HashSet<_> = entities.iter().collect();
1084        assert_eq!(unique.len(), 100);
1085
1086        // All entities should be alive
1087        for entity in &entities {
1088            assert!(allocator.is_alive(*entity));
1089        }
1090    }
1091
1092    #[test]
1093    fn test_allocator_first_generation_is_one() {
1094        let mut allocator = EntityAllocator::new();
1095
1096        // All newly allocated entities should have generation 1
1097        for _ in 0..10 {
1098            let entity = allocator.allocate();
1099            assert_eq!(entity.generation(), 1);
1100        }
1101    }
1102
1103    // -------------------------------------------------------------------------
1104    // Deallocation Tests
1105    // -------------------------------------------------------------------------
1106
1107    #[test]
1108    fn test_allocator_deallocate_basic() {
1109        let mut allocator = EntityAllocator::new();
1110        let entity = allocator.allocate();
1111
1112        assert!(allocator.is_alive(entity));
1113        assert!(allocator.deallocate(entity));
1114        assert!(!allocator.is_alive(entity));
1115    }
1116
1117    #[test]
1118    fn test_allocator_deallocate_returns_false_for_dead_entity() {
1119        let mut allocator = EntityAllocator::new();
1120        let entity = allocator.allocate();
1121
1122        // First deallocation succeeds
1123        assert!(allocator.deallocate(entity));
1124
1125        // Second deallocation fails
1126        assert!(!allocator.deallocate(entity));
1127    }
1128
1129    #[test]
1130    fn test_allocator_deallocate_returns_false_for_placeholder() {
1131        let mut allocator = EntityAllocator::new();
1132
1133        // PLACEHOLDER cannot be deallocated
1134        assert!(!allocator.deallocate(Entity::PLACEHOLDER));
1135    }
1136
1137    #[test]
1138    fn test_allocator_deallocate_returns_false_for_out_of_bounds() {
1139        let mut allocator = EntityAllocator::new();
1140        allocator.allocate(); // Allocate slot 0
1141
1142        // Entity with index 999 is out of bounds
1143        let fake_entity = Entity::new(999, 1);
1144        assert!(!allocator.deallocate(fake_entity));
1145    }
1146
1147    #[test]
1148    fn test_allocator_deallocate_returns_false_for_wrong_generation() {
1149        let mut allocator = EntityAllocator::new();
1150        let entity = allocator.allocate();
1151
1152        // Create a fake entity with same index but different generation
1153        let fake_entity = Entity::new(entity.index(), entity.generation() + 1);
1154        assert!(!allocator.deallocate(fake_entity));
1155
1156        // Original entity is still alive
1157        assert!(allocator.is_alive(entity));
1158    }
1159
1160    // -------------------------------------------------------------------------
1161    // is_alive Tests
1162    // -------------------------------------------------------------------------
1163
1164    #[test]
1165    fn test_allocator_is_alive() {
1166        let mut allocator = EntityAllocator::new();
1167        let entity = allocator.allocate();
1168
1169        assert!(allocator.is_alive(entity));
1170
1171        allocator.deallocate(entity);
1172        assert!(!allocator.is_alive(entity));
1173    }
1174
1175    #[test]
1176    fn test_allocator_is_alive_placeholder() {
1177        let allocator = EntityAllocator::new();
1178        assert!(!allocator.is_alive(Entity::PLACEHOLDER));
1179    }
1180
1181    #[test]
1182    fn test_allocator_is_alive_out_of_bounds() {
1183        let allocator = EntityAllocator::new();
1184        let fake_entity = Entity::new(999, 1);
1185        assert!(!allocator.is_alive(fake_entity));
1186    }
1187
1188    #[test]
1189    fn test_allocator_is_alive_wrong_generation() {
1190        let mut allocator = EntityAllocator::new();
1191        let entity = allocator.allocate();
1192
1193        // Wrong generation
1194        let stale = Entity::new(entity.index(), entity.generation() + 1);
1195        assert!(!allocator.is_alive(stale));
1196    }
1197
1198    // -------------------------------------------------------------------------
1199    // Slot Recycling Tests
1200    // -------------------------------------------------------------------------
1201
1202    #[test]
1203    fn test_allocator_recycling_basic() {
1204        let mut allocator = EntityAllocator::new();
1205
1206        // Allocate and deallocate
1207        let e1 = allocator.allocate();
1208        assert!(allocator.deallocate(e1));
1209
1210        // Allocate again - should reuse the slot
1211        let e2 = allocator.allocate();
1212
1213        // Same index, different generation
1214        assert_eq!(e1.index(), e2.index());
1215        assert_ne!(e1.generation(), e2.generation());
1216        assert_eq!(e2.generation(), 2); // Generation incremented
1217
1218        // e1 is dead, e2 is alive
1219        assert!(!allocator.is_alive(e1));
1220        assert!(allocator.is_alive(e2));
1221    }
1222
1223    #[test]
1224    fn test_allocator_recycling_multiple() {
1225        let mut allocator = EntityAllocator::new();
1226
1227        // Allocate 5 entities
1228        let entities: Vec<_> = (0..5).map(|_| allocator.allocate()).collect();
1229        assert_eq!(allocator.len(), 5);
1230        assert_eq!(allocator.capacity(), 5);
1231
1232        // Deallocate all
1233        for entity in &entities {
1234            assert!(allocator.deallocate(*entity));
1235        }
1236        assert_eq!(allocator.len(), 0);
1237        assert_eq!(allocator.capacity(), 5); // Capacity unchanged
1238
1239        // Allocate 5 more - should reuse slots
1240        let new_entities: Vec<_> = (0..5).map(|_| allocator.allocate()).collect();
1241        assert_eq!(allocator.len(), 5);
1242        assert_eq!(allocator.capacity(), 5); // Still 5, no new slots created
1243
1244        // All new entities should have generation 2
1245        for entity in &new_entities {
1246            assert_eq!(entity.generation(), 2);
1247        }
1248
1249        // Old entities are dead, new ones are alive
1250        for entity in &entities {
1251            assert!(!allocator.is_alive(*entity));
1252        }
1253        for entity in &new_entities {
1254            assert!(allocator.is_alive(*entity));
1255        }
1256    }
1257
1258    #[test]
1259    fn test_allocator_recycling_lifo_order() {
1260        let mut allocator = EntityAllocator::new();
1261
1262        // Allocate 3 entities
1263        let e0 = allocator.allocate();
1264        let e1 = allocator.allocate();
1265        let e2 = allocator.allocate();
1266
1267        // Deallocate in order: e0, e1, e2
1268        allocator.deallocate(e0);
1269        allocator.deallocate(e1);
1270        allocator.deallocate(e2);
1271
1272        // Reallocate - should come back in reverse order (LIFO)
1273        let new_e2 = allocator.allocate(); // Should reuse e2's slot
1274        let new_e1 = allocator.allocate(); // Should reuse e1's slot
1275        let new_e0 = allocator.allocate(); // Should reuse e0's slot
1276
1277        assert_eq!(new_e2.index(), e2.index());
1278        assert_eq!(new_e1.index(), e1.index());
1279        assert_eq!(new_e0.index(), e0.index());
1280    }
1281
1282    #[test]
1283    fn test_allocator_generation_increment() {
1284        let mut allocator = EntityAllocator::new();
1285
1286        // Allocate and deallocate the same slot multiple times
1287        let mut last_gen = 0;
1288        for expected_gen in 1..=10 {
1289            let entity = allocator.allocate();
1290            assert_eq!(entity.index(), 0); // Always slot 0
1291            assert_eq!(entity.generation(), expected_gen);
1292            assert!(entity.generation() > last_gen);
1293            last_gen = entity.generation();
1294
1295            allocator.deallocate(entity);
1296        }
1297    }
1298
1299    // -------------------------------------------------------------------------
1300    // len(), capacity(), is_empty() Tests
1301    // -------------------------------------------------------------------------
1302
1303    #[test]
1304    fn test_allocator_len() {
1305        let mut allocator = EntityAllocator::new();
1306        assert_eq!(allocator.len(), 0);
1307
1308        let e1 = allocator.allocate();
1309        assert_eq!(allocator.len(), 1);
1310
1311        let e2 = allocator.allocate();
1312        assert_eq!(allocator.len(), 2);
1313
1314        allocator.deallocate(e1);
1315        assert_eq!(allocator.len(), 1);
1316
1317        allocator.deallocate(e2);
1318        assert_eq!(allocator.len(), 0);
1319    }
1320
1321    #[test]
1322    fn test_allocator_capacity() {
1323        let mut allocator = EntityAllocator::new();
1324        assert_eq!(allocator.capacity(), 0);
1325
1326        let e1 = allocator.allocate();
1327        assert_eq!(allocator.capacity(), 1);
1328
1329        let e2 = allocator.allocate();
1330        assert_eq!(allocator.capacity(), 2);
1331
1332        // Capacity doesn't decrease on deallocation
1333        allocator.deallocate(e1);
1334        assert_eq!(allocator.capacity(), 2);
1335
1336        allocator.deallocate(e2);
1337        assert_eq!(allocator.capacity(), 2);
1338
1339        // Reusing slots doesn't increase capacity
1340        allocator.allocate();
1341        allocator.allocate();
1342        assert_eq!(allocator.capacity(), 2);
1343
1344        // New allocation beyond capacity increases it
1345        allocator.allocate();
1346        assert_eq!(allocator.capacity(), 3);
1347    }
1348
1349    #[test]
1350    fn test_allocator_is_empty() {
1351        let mut allocator = EntityAllocator::new();
1352        assert!(allocator.is_empty());
1353
1354        let entity = allocator.allocate();
1355        assert!(!allocator.is_empty());
1356
1357        allocator.deallocate(entity);
1358        assert!(allocator.is_empty());
1359    }
1360
1361    // -------------------------------------------------------------------------
1362    // Edge Cases and Stress Tests
1363    // -------------------------------------------------------------------------
1364
1365    #[test]
1366    fn test_allocator_many_allocations() {
1367        let mut allocator = EntityAllocator::new();
1368        const COUNT: usize = 10_000;
1369
1370        // Allocate many entities
1371        let entities: Vec<_> = (0..COUNT).map(|_| allocator.allocate()).collect();
1372        assert_eq!(allocator.len(), COUNT);
1373
1374        // All should be alive
1375        for entity in &entities {
1376            assert!(allocator.is_alive(*entity));
1377        }
1378
1379        // Deallocate half
1380        for entity in entities.iter().take(COUNT / 2) {
1381            allocator.deallocate(*entity);
1382        }
1383        assert_eq!(allocator.len(), COUNT / 2);
1384
1385        // Deallocated ones are dead
1386        for entity in entities.iter().take(COUNT / 2) {
1387            assert!(!allocator.is_alive(*entity));
1388        }
1389
1390        // Remaining are alive
1391        for entity in entities.iter().skip(COUNT / 2) {
1392            assert!(allocator.is_alive(*entity));
1393        }
1394    }
1395
1396    #[test]
1397    fn test_allocator_stress_allocate_deallocate_cycle() {
1398        let mut allocator = EntityAllocator::new();
1399        const CYCLES: usize = 100;
1400        const ENTITIES_PER_CYCLE: usize = 100;
1401
1402        for _ in 0..CYCLES {
1403            // Allocate
1404            let entities: Vec<_> = (0..ENTITIES_PER_CYCLE)
1405                .map(|_| allocator.allocate())
1406                .collect();
1407
1408            // Verify all alive
1409            for entity in &entities {
1410                assert!(allocator.is_alive(*entity));
1411            }
1412
1413            // Deallocate all
1414            for entity in &entities {
1415                assert!(allocator.deallocate(*entity));
1416            }
1417
1418            // Verify all dead
1419            for entity in &entities {
1420                assert!(!allocator.is_alive(*entity));
1421            }
1422        }
1423
1424        // After all cycles, should be empty but have capacity
1425        assert!(allocator.is_empty());
1426        assert_eq!(allocator.capacity(), ENTITIES_PER_CYCLE);
1427    }
1428
1429    #[test]
1430    fn test_allocator_unique_entities() {
1431        let mut allocator = EntityAllocator::new();
1432        let mut seen = HashSet::new();
1433
1434        // Allocate, deallocate, and reallocate many times
1435        for _ in 0..1000 {
1436            let entity = allocator.allocate();
1437
1438            // Each entity should be unique (index + generation combination)
1439            let key = entity.to_bits();
1440            assert!(
1441                seen.insert(key),
1442                "Duplicate entity: {:?} (bits: {})",
1443                entity,
1444                key
1445            );
1446
1447            // 50% chance of deallocating
1448            if seen.len() % 2 == 0 {
1449                allocator.deallocate(entity);
1450            }
1451        }
1452    }
1453
1454    // =========================================================================
1455    // Bulk Operations Tests
1456    // =========================================================================
1457
1458    #[test]
1459    fn test_allocate_batch_empty() {
1460        let mut allocator = EntityAllocator::new();
1461
1462        let entities = allocator.allocate_batch(0);
1463        assert!(entities.is_empty());
1464        assert_eq!(allocator.len(), 0);
1465        assert_eq!(allocator.capacity(), 0);
1466    }
1467
1468    #[test]
1469    fn test_allocate_batch_basic() {
1470        let mut allocator = EntityAllocator::new();
1471
1472        let entities = allocator.allocate_batch(100);
1473        assert_eq!(entities.len(), 100);
1474        assert_eq!(allocator.len(), 100);
1475        assert_eq!(allocator.capacity(), 100);
1476
1477        // All entities should be unique
1478        let unique: HashSet<_> = entities.iter().collect();
1479        assert_eq!(unique.len(), 100);
1480
1481        // All entities should be alive
1482        for entity in &entities {
1483            assert!(allocator.is_alive(*entity));
1484            assert!(!entity.is_placeholder());
1485        }
1486
1487        // All should have generation 1 (first allocation)
1488        for entity in &entities {
1489            assert_eq!(entity.generation(), 1);
1490        }
1491    }
1492
1493    #[test]
1494    fn test_allocate_batch_reuses_free_slots() {
1495        let mut allocator = EntityAllocator::new();
1496
1497        // Allocate 50 entities, then deallocate them all
1498        let first_batch = allocator.allocate_batch(50);
1499        assert_eq!(allocator.len(), 50);
1500
1501        for entity in &first_batch {
1502            allocator.deallocate(*entity);
1503        }
1504        assert_eq!(allocator.len(), 0);
1505        assert_eq!(allocator.capacity(), 50);
1506
1507        // Allocate 50 more - should reuse all freed slots
1508        let second_batch = allocator.allocate_batch(50);
1509        assert_eq!(allocator.len(), 50);
1510        assert_eq!(allocator.capacity(), 50); // No new slots created
1511
1512        // All should have generation 2 (recycled)
1513        for entity in &second_batch {
1514            assert_eq!(entity.generation(), 2);
1515        }
1516
1517        // All original entities should be dead
1518        for entity in &first_batch {
1519            assert!(!allocator.is_alive(*entity));
1520        }
1521
1522        // All new entities should be alive
1523        for entity in &second_batch {
1524            assert!(allocator.is_alive(*entity));
1525        }
1526    }
1527
1528    #[test]
1529    fn test_allocate_batch_mixed_reuse_and_new() {
1530        let mut allocator = EntityAllocator::new();
1531
1532        // Allocate 30, deallocate all
1533        let first = allocator.allocate_batch(30);
1534        for e in &first {
1535            allocator.deallocate(*e);
1536        }
1537        assert_eq!(allocator.capacity(), 30);
1538
1539        // Now allocate 50 - should reuse 30, create 20 new
1540        let second = allocator.allocate_batch(50);
1541        assert_eq!(second.len(), 50);
1542        assert_eq!(allocator.len(), 50);
1543        assert_eq!(allocator.capacity(), 50); // Grew by 20
1544
1545        // Count generations
1546        let gen1_count = second.iter().filter(|e| e.generation() == 1).count();
1547        let gen2_count = second.iter().filter(|e| e.generation() == 2).count();
1548
1549        assert_eq!(gen1_count, 20); // New slots
1550        assert_eq!(gen2_count, 30); // Recycled slots
1551    }
1552
1553    #[test]
1554    fn test_allocate_batch_large() {
1555        let mut allocator = EntityAllocator::new();
1556
1557        let entities = allocator.allocate_batch(10_000);
1558        assert_eq!(entities.len(), 10_000);
1559        assert_eq!(allocator.len(), 10_000);
1560
1561        // All should be unique
1562        let unique: HashSet<_> = entities.iter().collect();
1563        assert_eq!(unique.len(), 10_000);
1564
1565        // All should be alive
1566        for entity in &entities {
1567            assert!(allocator.is_alive(*entity));
1568        }
1569    }
1570
1571    #[test]
1572    fn test_deallocate_batch_empty() {
1573        let mut allocator = EntityAllocator::new();
1574
1575        let deallocated = allocator.deallocate_batch(&[]);
1576        assert_eq!(deallocated, 0);
1577    }
1578
1579    #[test]
1580    fn test_deallocate_batch_basic() {
1581        let mut allocator = EntityAllocator::new();
1582
1583        let entities = allocator.allocate_batch(100);
1584        assert_eq!(allocator.len(), 100);
1585
1586        let deallocated = allocator.deallocate_batch(&entities);
1587        assert_eq!(deallocated, 100);
1588        assert_eq!(allocator.len(), 0);
1589
1590        // All should be dead
1591        for entity in &entities {
1592            assert!(!allocator.is_alive(*entity));
1593        }
1594    }
1595
1596    #[test]
1597    fn test_deallocate_batch_partial_invalid() {
1598        let mut allocator = EntityAllocator::new();
1599
1600        let entities = allocator.allocate_batch(10);
1601
1602        // Deallocate some individually first
1603        allocator.deallocate(entities[0]);
1604        allocator.deallocate(entities[2]);
1605        allocator.deallocate(entities[4]);
1606
1607        // Now batch deallocate all - should only succeed for 7
1608        let deallocated = allocator.deallocate_batch(&entities);
1609        assert_eq!(deallocated, 7); // 10 - 3 already deallocated
1610
1611        // All should be dead now
1612        for entity in &entities {
1613            assert!(!allocator.is_alive(*entity));
1614        }
1615    }
1616
1617    #[test]
1618    fn test_deallocate_batch_all_invalid() {
1619        let mut allocator = EntityAllocator::new();
1620
1621        let entities = allocator.allocate_batch(10);
1622
1623        // Deallocate all individually
1624        for entity in &entities {
1625            allocator.deallocate(*entity);
1626        }
1627
1628        // Batch deallocate should return 0
1629        let deallocated = allocator.deallocate_batch(&entities);
1630        assert_eq!(deallocated, 0);
1631    }
1632
1633    #[test]
1634    fn test_deallocate_batch_with_placeholder() {
1635        let mut allocator = EntityAllocator::new();
1636
1637        let entities = allocator.allocate_batch(5);
1638        let mut with_placeholder: Vec<Entity> = entities.clone();
1639        with_placeholder.push(Entity::PLACEHOLDER);
1640        with_placeholder.push(Entity::PLACEHOLDER);
1641
1642        let deallocated = allocator.deallocate_batch(&with_placeholder);
1643        assert_eq!(deallocated, 5); // Only the 5 valid entities
1644
1645        assert!(allocator.is_empty());
1646    }
1647
1648    #[test]
1649    fn test_deallocate_batch_with_out_of_bounds() {
1650        let mut allocator = EntityAllocator::new();
1651
1652        let entities = allocator.allocate_batch(5);
1653        let mut with_invalid: Vec<Entity> = entities.clone();
1654        with_invalid.push(Entity::new(9999, 1)); // Out of bounds
1655        with_invalid.push(Entity::new(10000, 1)); // Out of bounds
1656
1657        let deallocated = allocator.deallocate_batch(&with_invalid);
1658        assert_eq!(deallocated, 5); // Only the 5 valid entities
1659    }
1660
1661    #[test]
1662    fn test_reserve_basic() {
1663        let mut allocator = EntityAllocator::new();
1664
1665        allocator.reserve(1000);
1666
1667        // No entities allocated, but memory reserved
1668        assert_eq!(allocator.len(), 0);
1669        assert_eq!(allocator.capacity(), 0);
1670
1671        // Now allocate - no reallocation should occur
1672        let entities = allocator.allocate_batch(500);
1673        assert_eq!(entities.len(), 500);
1674        assert_eq!(allocator.len(), 500);
1675    }
1676
1677    #[test]
1678    fn test_reserve_after_allocations() {
1679        let mut allocator = EntityAllocator::new();
1680
1681        // Allocate some entities first
1682        let _first = allocator.allocate_batch(100);
1683        assert_eq!(allocator.capacity(), 100);
1684
1685        // Reserve more
1686        allocator.reserve(1000);
1687
1688        // Allocate many more
1689        let second = allocator.allocate_batch(1000);
1690        assert_eq!(second.len(), 1000);
1691        assert_eq!(allocator.len(), 1100);
1692    }
1693
1694    #[test]
1695    fn test_reserve_zero() {
1696        let mut allocator = EntityAllocator::new();
1697
1698        allocator.reserve(0);
1699        assert_eq!(allocator.len(), 0);
1700        assert_eq!(allocator.capacity(), 0);
1701    }
1702
1703    #[test]
1704    fn test_batch_stress_test() {
1705        let mut allocator = EntityAllocator::new();
1706        const BATCH_SIZE: usize = 1000;
1707        const ITERATIONS: usize = 100;
1708
1709        for _ in 0..ITERATIONS {
1710            // Allocate batch
1711            let entities = allocator.allocate_batch(BATCH_SIZE);
1712            assert_eq!(entities.len(), BATCH_SIZE);
1713
1714            // Verify all alive
1715            for entity in &entities {
1716                assert!(allocator.is_alive(*entity));
1717            }
1718
1719            // Deallocate batch
1720            let deallocated = allocator.deallocate_batch(&entities);
1721            assert_eq!(deallocated, BATCH_SIZE);
1722
1723            // Verify all dead
1724            for entity in &entities {
1725                assert!(!allocator.is_alive(*entity));
1726            }
1727        }
1728
1729        // After all iterations, should be empty
1730        assert!(allocator.is_empty());
1731
1732        // Capacity should be exactly BATCH_SIZE (slots reused each iteration)
1733        assert_eq!(allocator.capacity(), BATCH_SIZE);
1734    }
1735
1736    #[test]
1737    fn test_batch_vs_individual_equivalence() {
1738        // Verify that batch operations produce equivalent results to individual ops
1739
1740        let mut batch_allocator = EntityAllocator::new();
1741        let mut individual_allocator = EntityAllocator::new();
1742
1743        // Allocate same count via batch vs individual
1744        let batch_entities = batch_allocator.allocate_batch(100);
1745        let individual_entities: Vec<_> =
1746            (0..100).map(|_| individual_allocator.allocate()).collect();
1747
1748        assert_eq!(batch_allocator.len(), individual_allocator.len());
1749        assert_eq!(batch_allocator.capacity(), individual_allocator.capacity());
1750
1751        // Same number of unique entities
1752        assert_eq!(batch_entities.len(), individual_entities.len());
1753
1754        // All entities should match in structure (index 0-99, generation 1)
1755        for i in 0..100 {
1756            // Since both allocate sequentially with no free slots, indices match
1757            assert_eq!(batch_entities[i].index() as usize, i);
1758            assert_eq!(individual_entities[i].index() as usize, i);
1759            assert_eq!(batch_entities[i].generation(), 1);
1760            assert_eq!(individual_entities[i].generation(), 1);
1761        }
1762
1763        // Deallocate via batch vs individual
1764        let batch_count = batch_allocator.deallocate_batch(&batch_entities);
1765        let individual_count = individual_entities
1766            .iter()
1767            .filter(|e| individual_allocator.deallocate(**e))
1768            .count();
1769
1770        assert_eq!(batch_count, individual_count);
1771        assert_eq!(batch_allocator.len(), individual_allocator.len());
1772    }
1773}