Skip to main content

galeon_engine/
archetype.rs

1// SPDX-License-Identifier: AGPL-3.0-only OR Commercial
2
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5
6use crate::entity::Entity;
7
8// ---------------------------------------------------------------------------
9// ArchetypeId
10// ---------------------------------------------------------------------------
11
12/// Index into the archetype store.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct ArchetypeId(pub(crate) u32);
15
16impl ArchetypeId {
17    /// Returns the raw index.
18    pub fn index(self) -> u32 {
19        self.0
20    }
21}
22
23// ---------------------------------------------------------------------------
24// EntityLocation
25// ---------------------------------------------------------------------------
26
27/// Where an entity lives inside archetype storage.
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct EntityLocation {
30    pub archetype_id: ArchetypeId,
31    pub row: u32,
32}
33
34// ---------------------------------------------------------------------------
35// ArchetypeLayout
36// ---------------------------------------------------------------------------
37
38/// Sorted set of `TypeId`s identifying which components an archetype holds.
39///
40/// Two layouts with the same type set (regardless of input order) are equal
41/// and hash the same.
42#[derive(Clone, Debug, PartialEq, Eq, Hash)]
43pub struct ArchetypeLayout {
44    /// Always kept sorted and deduplicated.
45    type_ids: Vec<TypeId>,
46}
47
48impl ArchetypeLayout {
49    /// Create a layout from an unsorted, possibly-duplicate slice of type IDs.
50    pub fn from_type_ids(ids: &[TypeId]) -> Self {
51        let mut sorted = ids.to_vec();
52        sorted.sort();
53        sorted.dedup();
54        Self { type_ids: sorted }
55    }
56
57    /// Create an empty layout (the "void" archetype for entities with no components).
58    pub fn empty() -> Self {
59        Self {
60            type_ids: Vec::new(),
61        }
62    }
63
64    /// Whether this layout contains the given type.
65    pub fn contains(&self, id: TypeId) -> bool {
66        self.type_ids.binary_search(&id).is_ok()
67    }
68
69    /// Return a new layout with `id` added (no-op if already present).
70    pub fn with_added(&self, id: TypeId) -> Self {
71        if self.contains(id) {
72            return self.clone();
73        }
74        let mut ids = self.type_ids.clone();
75        let pos = ids.binary_search(&id).unwrap_err();
76        ids.insert(pos, id);
77        Self { type_ids: ids }
78    }
79
80    /// Return a new layout with `id` removed (no-op if absent).
81    pub fn with_removed(&self, id: TypeId) -> Self {
82        if let Ok(pos) = self.type_ids.binary_search(&id) {
83            let mut ids = self.type_ids.clone();
84            ids.remove(pos);
85            Self { type_ids: ids }
86        } else {
87            self.clone()
88        }
89    }
90
91    /// The number of component types in this layout.
92    pub fn len(&self) -> usize {
93        self.type_ids.len()
94    }
95
96    /// Whether this layout has zero component types.
97    pub fn is_empty(&self) -> bool {
98        self.type_ids.is_empty()
99    }
100
101    /// Iterate the type IDs in sorted order.
102    pub fn iter(&self) -> impl Iterator<Item = TypeId> + '_ {
103        self.type_ids.iter().copied()
104    }
105}
106
107// ---------------------------------------------------------------------------
108// AnyColumn trait + Column<T>
109// ---------------------------------------------------------------------------
110
111/// Type-erased column operations. Each archetype column implements this.
112#[allow(clippy::len_without_is_empty)]
113pub trait AnyColumn: Any + Send + Sync {
114    /// Swap-remove a row. The data is dropped.
115    fn swap_remove_and_drop(&mut self, row: usize);
116
117    /// Move the data at `row` in this column into `dst` (which must be the
118    /// same concrete `Column<T>`). The row is swap-removed from `self`.
119    fn move_to(&mut self, row: usize, dst: &mut dyn AnyColumn);
120
121    /// Number of rows.
122    fn len(&self) -> usize;
123
124    /// Create an empty column of the same concrete type.
125    fn new_empty(&self) -> Box<dyn AnyColumn>;
126
127    /// Stamp both tick vectors at `row`.
128    fn stamp_ticks(&mut self, row: usize, added: u64, changed: u64);
129
130    // Down-casting helpers.
131    fn as_any(&self) -> &dyn Any;
132    fn as_any_mut(&mut self) -> &mut dyn Any;
133}
134
135/// Typed dense column for a single component type within one archetype.
136///
137/// Each row has parallel `added_ticks` and `changed_ticks` entries for
138/// change detection. Tick 0 is the sentinel meaning "never observed".
139pub struct Column<T> {
140    data: Vec<T>,
141    added_ticks: Vec<u64>,
142    changed_ticks: Vec<u64>,
143}
144
145impl<T: Send + Sync + 'static> Default for Column<T> {
146    fn default() -> Self {
147        Self::new()
148    }
149}
150
151impl<T: Send + Sync + 'static> Column<T> {
152    /// Create an empty column.
153    pub fn new() -> Self {
154        Self {
155            data: Vec::new(),
156            added_ticks: Vec::new(),
157            changed_ticks: Vec::new(),
158        }
159    }
160
161    /// Append a value at the end with sentinel ticks (0).
162    pub fn push(&mut self, value: T) {
163        self.data.push(value);
164        self.added_ticks.push(0);
165        self.changed_ticks.push(0);
166    }
167
168    /// Append a value with explicit tick stamps.
169    pub fn push_with_ticks(&mut self, value: T, added: u64, changed: u64) {
170        self.data.push(value);
171        self.added_ticks.push(added);
172        self.changed_ticks.push(changed);
173    }
174
175    /// Get an immutable reference by row index.
176    pub fn get(&self, row: usize) -> Option<&T> {
177        self.data.get(row)
178    }
179
180    /// Get a mutable reference by row index.
181    pub fn get_mut(&mut self, row: usize) -> Option<&mut T> {
182        self.data.get_mut(row)
183    }
184
185    /// Swap-remove and return the value at `row`.
186    /// Tick vectors are kept in sync.
187    pub fn swap_remove(&mut self, row: usize) -> T {
188        self.added_ticks.swap_remove(row);
189        self.changed_ticks.swap_remove(row);
190        self.data.swap_remove(row)
191    }
192
193    /// Number of rows.
194    pub fn len(&self) -> usize {
195        self.data.len()
196    }
197
198    /// Whether the column is empty.
199    pub fn is_empty(&self) -> bool {
200        self.data.is_empty()
201    }
202
203    /// Iterate all values immutably.
204    pub fn iter(&self) -> impl Iterator<Item = &T> {
205        self.data.iter()
206    }
207
208    /// Iterate all values mutably.
209    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
210        self.data.iter_mut()
211    }
212
213    /// Returns a raw mutable pointer to the column data.
214    pub(crate) fn as_mut_ptr(&mut self) -> *mut T {
215        self.data.as_mut_ptr()
216    }
217
218    // -- Tick accessors -------------------------------------------------------
219
220    /// The added tick for `row` (0 = sentinel / never stamped).
221    pub fn added_tick(&self, row: usize) -> u64 {
222        self.added_ticks[row]
223    }
224
225    /// The changed tick for `row` (0 = sentinel / never stamped).
226    pub fn changed_tick(&self, row: usize) -> u64 {
227        self.changed_ticks[row]
228    }
229
230    /// Stamp the added tick for `row`.
231    pub fn set_added_tick(&mut self, row: usize, tick: u64) {
232        self.added_ticks[row] = tick;
233    }
234
235    /// Stamp the changed tick for `row`.
236    pub fn set_changed_tick(&mut self, row: usize, tick: u64) {
237        self.changed_ticks[row] = tick;
238    }
239
240    /// Raw pointer to the changed-ticks vector (for mutable query stamping).
241    pub(crate) fn changed_ticks_mut_ptr(&mut self) -> *mut u64 {
242        self.changed_ticks.as_mut_ptr()
243    }
244
245    /// Slice of added ticks (parallel to data rows).
246    pub fn added_ticks(&self) -> &[u64] {
247        &self.added_ticks
248    }
249
250    /// Slice of changed ticks (parallel to data rows).
251    pub fn changed_ticks(&self) -> &[u64] {
252        &self.changed_ticks
253    }
254}
255
256impl<T: Send + Sync + 'static> AnyColumn for Column<T> {
257    fn swap_remove_and_drop(&mut self, row: usize) {
258        self.data.swap_remove(row);
259        self.added_ticks.swap_remove(row);
260        self.changed_ticks.swap_remove(row);
261    }
262
263    fn move_to(&mut self, row: usize, dst: &mut dyn AnyColumn) {
264        let value = self.data.swap_remove(row);
265        let added = self.added_ticks.swap_remove(row);
266        let changed = self.changed_ticks.swap_remove(row);
267        let dst_typed = dst
268            .as_any_mut()
269            .downcast_mut::<Column<T>>()
270            .expect("move_to: column type mismatch");
271        dst_typed.data.push(value);
272        dst_typed.added_ticks.push(added);
273        dst_typed.changed_ticks.push(changed);
274    }
275
276    fn len(&self) -> usize {
277        self.data.len()
278    }
279
280    fn new_empty(&self) -> Box<dyn AnyColumn> {
281        Box::new(Column::<T>::new())
282    }
283
284    fn stamp_ticks(&mut self, row: usize, added: u64, changed: u64) {
285        self.added_ticks[row] = added;
286        self.changed_ticks[row] = changed;
287    }
288
289    fn as_any(&self) -> &dyn Any {
290        self
291    }
292
293    fn as_any_mut(&mut self) -> &mut dyn Any {
294        self
295    }
296}
297
298// ---------------------------------------------------------------------------
299// ArchetypeEdge
300// ---------------------------------------------------------------------------
301
302/// Cached archetype transition when a component is added or removed.
303#[derive(Clone, Debug, Default)]
304pub struct ArchetypeEdge {
305    /// Archetype reached by adding a component of this type.
306    pub add: Option<ArchetypeId>,
307    /// Archetype reached by removing a component of this type.
308    pub remove: Option<ArchetypeId>,
309}
310
311type RequiredOptionalColumnsMut<'a, A, B> =
312    (&'a [Entity], &'a mut Column<A>, Option<&'a mut Column<B>>);
313
314type ThreeColumnsMut<'a, A, B, C> = (
315    &'a [Entity],
316    &'a mut Column<A>,
317    &'a mut Column<B>,
318    &'a mut Column<C>,
319);
320
321// ---------------------------------------------------------------------------
322// Archetype
323// ---------------------------------------------------------------------------
324
325/// A group of entities that share the same set of component types.
326///
327/// Columns are independently borrowable — no double-borrow of a `HashMap`
328/// needed for multi-component queries.
329pub struct Archetype {
330    id: ArchetypeId,
331    layout: ArchetypeLayout,
332    /// Entity handles stored in insertion order; indices are row numbers.
333    entities: Vec<Entity>,
334    /// One dense column per component type in the layout.
335    columns: HashMap<TypeId, Box<dyn AnyColumn>>,
336    /// Lazy edge cache for archetype transitions.
337    edges: HashMap<TypeId, ArchetypeEdge>,
338}
339
340impl Archetype {
341    /// Create a new empty archetype. Each type in `layout` gets an empty column
342    /// created by `column_factories` — one factory per `TypeId`.
343    ///
344    /// `column_factories` maps each `TypeId` to a function that produces an
345    /// empty `Box<dyn AnyColumn>` of the right concrete type. The caller
346    /// must provide a factory for every type in the layout.
347    pub(crate) fn new(
348        id: ArchetypeId,
349        layout: ArchetypeLayout,
350        column_factories: &HashMap<TypeId, Box<dyn AnyColumn>>,
351    ) -> Self {
352        let mut columns = HashMap::new();
353        for tid in layout.iter() {
354            let template = column_factories
355                .get(&tid)
356                .unwrap_or_else(|| panic!("no column factory for {:?}", tid));
357            columns.insert(tid, template.new_empty());
358        }
359        Self {
360            id,
361            layout,
362            entities: Vec::new(),
363            columns,
364            edges: HashMap::new(),
365        }
366    }
367
368    /// The archetype's ID.
369    pub fn id(&self) -> ArchetypeId {
370        self.id
371    }
372
373    /// The archetype's layout.
374    pub fn layout(&self) -> &ArchetypeLayout {
375        &self.layout
376    }
377
378    /// Number of entities in this archetype.
379    pub fn len(&self) -> usize {
380        self.entities.len()
381    }
382
383    /// Whether this archetype has no entities.
384    pub fn is_empty(&self) -> bool {
385        self.entities.is_empty()
386    }
387
388    /// The entity stored at `row`.
389    pub fn entity_at(&self, row: usize) -> Entity {
390        self.entities[row]
391    }
392
393    /// Slice of all entities in this archetype.
394    pub fn entities(&self) -> &[Entity] {
395        &self.entities
396    }
397
398    /// Get a typed column reference. Returns `None` if the type isn't in this
399    /// archetype's layout.
400    pub fn column<T: Send + Sync + 'static>(&self) -> Option<&Column<T>> {
401        let col = self.columns.get(&TypeId::of::<T>())?;
402        col.as_any().downcast_ref::<Column<T>>()
403    }
404
405    /// Get a typed column mutable reference.
406    pub fn column_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut Column<T>> {
407        let col = self.columns.get_mut(&TypeId::of::<T>())?;
408        col.as_any_mut().downcast_mut::<Column<T>>()
409    }
410
411    /// Get a type-erased column reference.
412    pub fn column_raw(&self, type_id: TypeId) -> Option<&dyn AnyColumn> {
413        self.columns.get(&type_id).map(|c| &**c)
414    }
415
416    /// Get a type-erased column mutable reference.
417    pub fn column_raw_mut(&mut self, type_id: TypeId) -> Option<&mut dyn AnyColumn> {
418        self.columns.get_mut(&type_id).map(|c| &mut **c)
419    }
420
421    /// Append an entity. The caller must push one value into every column
422    /// *before* calling this (or use the higher-level `ArchetypeStore` API).
423    ///
424    /// Returns the row index where the entity was placed.
425    pub(crate) fn push_entity(&mut self, entity: Entity) -> u32 {
426        let row = self.entities.len() as u32;
427        self.entities.push(entity);
428        row
429    }
430
431    /// Swap-remove the entity at `row`, dropping all its component data.
432    /// Returns the entity that was removed *and* the entity that was moved
433    /// into `row` (if any — `None` when the removed entity was the last).
434    pub(crate) fn swap_remove_entity(&mut self, row: usize) -> (Entity, Option<Entity>) {
435        let removed = self.entities.swap_remove(row);
436        let moved = if row < self.entities.len() {
437            Some(self.entities[row])
438        } else {
439            None
440        };
441        for col in self.columns.values_mut() {
442            col.swap_remove_and_drop(row);
443        }
444        (removed, moved)
445    }
446
447    /// Swap-remove only the entity entry at `row`, WITHOUT touching columns.
448    ///
449    /// The caller must have already moved/removed column data for this row.
450    /// Returns the removed entity and the entity that was swapped into `row` (if any).
451    pub(crate) fn swap_remove_entity_entry(&mut self, row: usize) -> (Entity, Option<Entity>) {
452        let removed = self.entities.swap_remove(row);
453        let moved = if row < self.entities.len() {
454            Some(self.entities[row])
455        } else {
456            None
457        };
458        (removed, moved)
459    }
460
461    /// Split-borrow: shared entity slice + exclusive column reference.
462    ///
463    /// Returns `None` if the archetype doesn't contain type `T`.
464    pub fn entities_and_column_mut<T: Send + Sync + 'static>(
465        &mut self,
466    ) -> Option<(&[Entity], &mut Column<T>)> {
467        let Self {
468            entities, columns, ..
469        } = self;
470        let col = columns.get_mut(&TypeId::of::<T>())?;
471        let col_typed = col.as_any_mut().downcast_mut::<Column<T>>()?;
472        Some((entities, col_typed))
473    }
474
475    /// Split-borrow: shared entity slice + two exclusive column references.
476    ///
477    /// Panics if `A == B`. Returns `None` if either type is absent.
478    pub fn entities_and_two_columns_mut<A, B>(
479        &mut self,
480    ) -> Option<(&[Entity], &mut Column<A>, &mut Column<B>)>
481    where
482        A: Send + Sync + 'static,
483        B: Send + Sync + 'static,
484    {
485        assert_ne!(
486            TypeId::of::<A>(),
487            TypeId::of::<B>(),
488            "cannot borrow the same column mutably twice"
489        );
490        let Self {
491            entities, columns, ..
492        } = self;
493        let ptr = columns as *mut HashMap<TypeId, Box<dyn AnyColumn>>;
494        // SAFETY: A != B (asserted above), so `get_mut` returns pointers to two
495        // distinct Box<dyn AnyColumn> heap allocations. The two temporary &mut to
496        // HashMap entries access different keys, and because values are boxed
497        // (heap-allocated), the returned &mut Column<T> references point into
498        // separate allocations with no overlap. No insertion occurs, so no
499        // reallocation can invalidate either pointer.
500        unsafe {
501            let col_a = (*ptr)
502                .get_mut(&TypeId::of::<A>())?
503                .as_any_mut()
504                .downcast_mut::<Column<A>>()?;
505            let col_b = (*ptr)
506                .get_mut(&TypeId::of::<B>())?
507                .as_any_mut()
508                .downcast_mut::<Column<B>>()?;
509            Some((&*entities, col_a, col_b))
510        }
511    }
512
513    /// Split-borrow: shared entity slice + required column A + optional column B.
514    ///
515    /// Panics if `A == B`. Returns `None` if required type A is absent.
516    /// Optional type B may be absent (returns `None` in the Option).
517    pub(crate) fn entities_and_required_optional_columns_mut<A, B>(
518        &mut self,
519    ) -> Option<RequiredOptionalColumnsMut<'_, A, B>>
520    where
521        A: Send + Sync + 'static,
522        B: Send + Sync + 'static,
523    {
524        assert_ne!(
525            TypeId::of::<A>(),
526            TypeId::of::<B>(),
527            "cannot borrow the same column mutably twice"
528        );
529        let Self {
530            entities, columns, ..
531        } = self;
532        let ptr = columns as *mut HashMap<TypeId, Box<dyn AnyColumn>>;
533        // SAFETY: A != B (asserted above), so if both columns exist, `get_mut`
534        // returns pointers to two distinct Box<dyn AnyColumn> heap allocations.
535        // The two temporary &mut to HashMap entries access different keys, and
536        // because values are boxed (heap-allocated), the returned &mut Column<T>
537        // references point into separate allocations with no overlap. No insertion
538        // occurs, so no reallocation can invalidate either pointer.
539        unsafe {
540            let col_a = (*ptr)
541                .get_mut(&TypeId::of::<A>())?
542                .as_any_mut()
543                .downcast_mut::<Column<A>>()?;
544            let col_b = (*ptr)
545                .get_mut(&TypeId::of::<B>())
546                .and_then(|col| col.as_any_mut().downcast_mut::<Column<B>>());
547            Some((&*entities, col_a, col_b))
548        }
549    }
550
551    /// Split-borrow: shared entity slice + three exclusive column references.
552    ///
553    /// Panics if any two queried types are the same. Returns `None` if any
554    /// type is absent from this archetype.
555    pub fn entities_and_three_columns_mut<A, B, C>(
556        &mut self,
557    ) -> Option<ThreeColumnsMut<'_, A, B, C>>
558    where
559        A: Send + Sync + 'static,
560        B: Send + Sync + 'static,
561        C: Send + Sync + 'static,
562    {
563        assert_ne!(
564            TypeId::of::<A>(),
565            TypeId::of::<B>(),
566            "cannot borrow the same column mutably twice"
567        );
568        assert_ne!(
569            TypeId::of::<A>(),
570            TypeId::of::<C>(),
571            "cannot borrow the same column mutably twice"
572        );
573        assert_ne!(
574            TypeId::of::<B>(),
575            TypeId::of::<C>(),
576            "cannot borrow the same column mutably twice"
577        );
578
579        let Self {
580            entities, columns, ..
581        } = self;
582        let ptr = columns as *mut HashMap<TypeId, Box<dyn AnyColumn>>;
583        // SAFETY: A, B, and C are distinct (asserted above), so `get_mut`
584        // returns pointers to three distinct Box<dyn AnyColumn> heap
585        // allocations. The three temporary &mut to HashMap entries access
586        // different keys, and because values are boxed (heap-allocated), the
587        // returned &mut Column<T> references point into separate allocations
588        // with no overlap. No insertion occurs, so no reallocation can
589        // invalidate any pointer.
590        unsafe {
591            let col_a = (*ptr)
592                .get_mut(&TypeId::of::<A>())?
593                .as_any_mut()
594                .downcast_mut::<Column<A>>()?;
595            let col_b = (*ptr)
596                .get_mut(&TypeId::of::<B>())?
597                .as_any_mut()
598                .downcast_mut::<Column<B>>()?;
599            let col_c = (*ptr)
600                .get_mut(&TypeId::of::<C>())?
601                .as_any_mut()
602                .downcast_mut::<Column<C>>()?;
603            Some((&*entities, col_a, col_b, col_c))
604        }
605    }
606
607    /// Stamp added/changed ticks on every column at `row`.
608    pub(crate) fn stamp_all_ticks(&mut self, row: u32, added: u64, changed: u64) {
609        for col in self.columns.values_mut() {
610            col.stamp_ticks(row as usize, added, changed);
611        }
612    }
613
614    /// Stamp added/changed ticks on a single column at `row`.
615    pub(crate) fn stamp_column_ticks(
616        &mut self,
617        type_id: TypeId,
618        row: u32,
619        added: u64,
620        changed: u64,
621    ) {
622        if let Some(col) = self.columns.get_mut(&type_id) {
623            col.stamp_ticks(row as usize, added, changed);
624        }
625    }
626
627    /// Get the edge cache entry for a component type.
628    pub fn edge(&self, type_id: TypeId) -> Option<&ArchetypeEdge> {
629        self.edges.get(&type_id)
630    }
631
632    /// Insert or update an edge cache entry.
633    #[allow(dead_code)]
634    pub(crate) fn set_edge(&mut self, type_id: TypeId, edge: ArchetypeEdge) {
635        self.edges.insert(type_id, edge);
636    }
637}
638
639// ---------------------------------------------------------------------------
640// ArchetypeStore
641// ---------------------------------------------------------------------------
642
643/// Registry of all archetypes, indexed by layout.
644pub struct ArchetypeStore {
645    archetypes: Vec<Archetype>,
646    index: HashMap<ArchetypeLayout, ArchetypeId>,
647    /// Column factories: for each TypeId that has ever been seen, a prototype
648    /// `Box<dyn AnyColumn>` that can produce empty columns via `new_empty()`.
649    column_factories: HashMap<TypeId, Box<dyn AnyColumn>>,
650}
651
652impl ArchetypeStore {
653    /// Create an empty store.
654    pub fn new() -> Self {
655        Self {
656            archetypes: Vec::new(),
657            index: HashMap::new(),
658            column_factories: HashMap::new(),
659        }
660    }
661
662    /// Register a column factory for a component type. Must be called before
663    /// `get_or_create` is called with a layout containing that type.
664    pub fn register_column<T: Send + Sync + 'static>(&mut self) {
665        self.column_factories
666            .entry(TypeId::of::<T>())
667            .or_insert_with(|| Box::new(Column::<T>::new()));
668    }
669
670    /// Look up or create the archetype for `layout`.
671    pub fn get_or_create(&mut self, layout: ArchetypeLayout) -> ArchetypeId {
672        if let Some(&id) = self.index.get(&layout) {
673            return id;
674        }
675        let id = ArchetypeId(self.archetypes.len() as u32);
676        let archetype = Archetype::new(id, layout.clone(), &self.column_factories);
677        self.archetypes.push(archetype);
678        self.index.insert(layout, id);
679        id
680    }
681
682    /// Get an archetype by ID.
683    pub fn get(&self, id: ArchetypeId) -> &Archetype {
684        &self.archetypes[id.0 as usize]
685    }
686
687    /// Get an archetype by its dense store index.
688    pub(crate) fn get_by_index(&self, index: usize) -> Option<&Archetype> {
689        self.archetypes.get(index)
690    }
691
692    /// Get a mutable archetype by ID.
693    pub fn get_mut(&mut self, id: ArchetypeId) -> &mut Archetype {
694        &mut self.archetypes[id.0 as usize]
695    }
696
697    /// Get a mutable archetype by index through a raw `*mut Self` pointer,
698    /// without creating an intermediate `&mut ArchetypeStore`.
699    ///
700    /// Uses `addr_of_mut!` to reach the `archetypes` Vec directly, so the
701    /// only mutable reference created is `&mut Vec<Archetype>` (at the field
702    /// level), not `&mut ArchetypeStore` (which would cover the whole struct).
703    ///
704    /// # Safety
705    ///
706    /// - `this` must be a valid, non-null pointer to an `ArchetypeStore`.
707    /// - The returned `&mut Archetype` must not alias any other live reference
708    ///   to the same archetype.
709    pub(crate) unsafe fn get_by_index_mut_ptr<'w>(
710        this: *mut Self,
711        index: usize,
712    ) -> Option<&'w mut Archetype> {
713        unsafe {
714            let vec_ptr: *mut Vec<Archetype> = std::ptr::addr_of_mut!((*this).archetypes);
715            (&mut *vec_ptr).get_mut(index)
716        }
717    }
718
719    /// Number of archetypes.
720    pub fn len(&self) -> usize {
721        self.archetypes.len()
722    }
723
724    /// Whether the store has no archetypes.
725    pub fn is_empty(&self) -> bool {
726        self.archetypes.is_empty()
727    }
728
729    /// Iterate all archetypes.
730    pub fn iter(&self) -> impl Iterator<Item = &Archetype> {
731        self.archetypes.iter()
732    }
733
734    /// Iterate all archetypes mutably.
735    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Archetype> {
736        self.archetypes.iter_mut()
737    }
738
739    /// Get mutable references to two different archetypes simultaneously.
740    ///
741    /// Panics if `a == b`.
742    pub fn get_two_mut(
743        &mut self,
744        a: ArchetypeId,
745        b: ArchetypeId,
746    ) -> (&mut Archetype, &mut Archetype) {
747        assert_ne!(a, b, "get_two_mut called with same ArchetypeId");
748        let a_idx = a.0 as usize;
749        let b_idx = b.0 as usize;
750        if a_idx < b_idx {
751            let (left, right) = self.archetypes.split_at_mut(b_idx);
752            (&mut left[a_idx], &mut right[0])
753        } else {
754            let (left, right) = self.archetypes.split_at_mut(a_idx);
755            (&mut right[0], &mut left[b_idx])
756        }
757    }
758}
759
760impl Default for ArchetypeStore {
761    fn default() -> Self {
762        Self::new()
763    }
764}
765
766// ===========================================================================
767// Tests
768// ===========================================================================
769
770#[cfg(test)]
771mod tests {
772    use super::*;
773
774    // ---- ArchetypeLayout --------------------------------------------------
775
776    #[test]
777    fn layout_sorts_and_deduplicates() {
778        let a = TypeId::of::<u32>();
779        let b = TypeId::of::<f64>();
780        let l1 = ArchetypeLayout::from_type_ids(&[b, a, b, a]);
781        let l2 = ArchetypeLayout::from_type_ids(&[a, b]);
782        assert_eq!(l1, l2);
783        assert_eq!(l1.len(), 2);
784    }
785
786    #[test]
787    fn layout_contains() {
788        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<f64>()]);
789        assert!(layout.contains(TypeId::of::<u32>()));
790        assert!(layout.contains(TypeId::of::<f64>()));
791        assert!(!layout.contains(TypeId::of::<bool>()));
792    }
793
794    #[test]
795    fn layout_with_added_and_removed() {
796        let base = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
797        let added = base.with_added(TypeId::of::<f64>());
798        assert_eq!(added.len(), 2);
799        assert!(added.contains(TypeId::of::<f64>()));
800
801        // Adding an already-present type is a no-op.
802        let same = added.with_added(TypeId::of::<u32>());
803        assert_eq!(same, added);
804
805        let removed = added.with_removed(TypeId::of::<u32>());
806        assert_eq!(removed.len(), 1);
807        assert!(!removed.contains(TypeId::of::<u32>()));
808        assert!(removed.contains(TypeId::of::<f64>()));
809
810        // Removing an absent type is a no-op.
811        let same2 = removed.with_removed(TypeId::of::<bool>());
812        assert_eq!(same2, removed);
813    }
814
815    #[test]
816    fn layout_empty() {
817        let e = ArchetypeLayout::empty();
818        assert!(e.is_empty());
819        assert_eq!(e.len(), 0);
820    }
821
822    #[test]
823    fn layout_hash_eq_regardless_of_input_order() {
824        use std::hash::{DefaultHasher, Hash, Hasher};
825        let a = TypeId::of::<u32>();
826        let b = TypeId::of::<f64>();
827        let c = TypeId::of::<bool>();
828
829        let l1 = ArchetypeLayout::from_type_ids(&[c, a, b]);
830        let l2 = ArchetypeLayout::from_type_ids(&[b, c, a]);
831
832        assert_eq!(l1, l2);
833
834        let hash = |l: &ArchetypeLayout| {
835            let mut h = DefaultHasher::new();
836            l.hash(&mut h);
837            h.finish()
838        };
839        assert_eq!(hash(&l1), hash(&l2));
840    }
841
842    // ---- Column<T> --------------------------------------------------------
843
844    #[test]
845    fn column_push_get() {
846        let mut col = Column::<u32>::new();
847        col.push(10);
848        col.push(20);
849        col.push(30);
850        assert_eq!(col.len(), 3);
851        assert_eq!(*col.get(0).unwrap(), 10);
852        assert_eq!(*col.get(1).unwrap(), 20);
853        assert_eq!(*col.get(2).unwrap(), 30);
854    }
855
856    #[test]
857    fn column_get_mut() {
858        let mut col = Column::<u32>::new();
859        col.push(5);
860        *col.get_mut(0).unwrap() = 99;
861        assert_eq!(*col.get(0).unwrap(), 99);
862    }
863
864    #[test]
865    fn column_swap_remove() {
866        let mut col = Column::<&str>::new();
867        col.push("a");
868        col.push("b");
869        col.push("c");
870        let removed = col.swap_remove(0);
871        assert_eq!(removed, "a");
872        assert_eq!(col.len(), 2);
873        // "c" moved into row 0
874        assert_eq!(*col.get(0).unwrap(), "c");
875        assert_eq!(*col.get(1).unwrap(), "b");
876    }
877
878    #[test]
879    fn column_move_to() {
880        let mut src = Column::<u32>::new();
881        src.push(10);
882        src.push(20);
883        src.push(30);
884
885        let mut dst = Column::<u32>::new();
886        src.move_to(1, &mut dst); // moves 20, swap-removes from src (30 fills row 1)
887
888        assert_eq!(src.len(), 2);
889        assert_eq!(dst.len(), 1);
890        assert_eq!(*dst.get(0).unwrap(), 20);
891        assert_eq!(*src.get(0).unwrap(), 10);
892        assert_eq!(*src.get(1).unwrap(), 30);
893    }
894
895    #[test]
896    fn column_new_empty_produces_correct_type() {
897        let col = Column::<f64>::new();
898        let any_col: &dyn AnyColumn = &col;
899        let empty = any_col.new_empty();
900        assert_eq!(empty.len(), 0);
901        // Downcast succeeds to the same type.
902        assert!(empty.as_any().downcast_ref::<Column<f64>>().is_some());
903    }
904
905    // ---- Archetype + ArchetypeStore ---------------------------------------
906
907    #[test]
908    fn archetype_store_get_or_create_idempotent() {
909        let mut store = ArchetypeStore::new();
910        store.register_column::<u32>();
911        store.register_column::<f64>();
912
913        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<f64>()]);
914        let id1 = store.get_or_create(layout.clone());
915        let id2 = store.get_or_create(layout);
916        assert_eq!(id1, id2);
917        assert_eq!(store.len(), 1);
918    }
919
920    #[test]
921    fn archetype_push_and_remove_entity() {
922        let mut store = ArchetypeStore::new();
923        store.register_column::<u32>();
924        store.register_column::<String>();
925
926        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<String>()]);
927        let arch_id = store.get_or_create(layout);
928
929        let e0 = Entity {
930            index: 0,
931            generation: 0,
932        };
933        let e1 = Entity {
934            index: 1,
935            generation: 0,
936        };
937        let e2 = Entity {
938            index: 2,
939            generation: 0,
940        };
941
942        // Push three entities.
943        {
944            let arch = store.get_mut(arch_id);
945            arch.column_mut::<u32>().unwrap().push(10);
946            arch.column_mut::<String>()
947                .unwrap()
948                .push("hello".to_string());
949            arch.push_entity(e0);
950
951            arch.column_mut::<u32>().unwrap().push(20);
952            arch.column_mut::<String>()
953                .unwrap()
954                .push("world".to_string());
955            arch.push_entity(e1);
956
957            arch.column_mut::<u32>().unwrap().push(30);
958            arch.column_mut::<String>().unwrap().push("foo".to_string());
959            arch.push_entity(e2);
960
961            assert_eq!(arch.len(), 3);
962        }
963
964        // Remove entity at row 0 (e0). e2 (last) swaps into row 0.
965        {
966            let arch = store.get_mut(arch_id);
967            let (removed, moved) = arch.swap_remove_entity(0);
968            assert_eq!(removed, e0);
969            assert_eq!(moved, Some(e2)); // e2 moved into row 0
970
971            assert_eq!(arch.len(), 2);
972            assert_eq!(arch.entity_at(0), e2);
973            assert_eq!(arch.entity_at(1), e1);
974
975            // Column data consistent after swap-remove.
976            assert_eq!(*arch.column::<u32>().unwrap().get(0).unwrap(), 30);
977            assert_eq!(*arch.column::<u32>().unwrap().get(1).unwrap(), 20);
978        }
979    }
980
981    #[test]
982    fn archetype_remove_last_entity_returns_no_moved() {
983        let mut store = ArchetypeStore::new();
984        store.register_column::<u32>();
985
986        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
987        let arch_id = store.get_or_create(layout);
988
989        let e0 = Entity {
990            index: 0,
991            generation: 0,
992        };
993
994        let arch = store.get_mut(arch_id);
995        arch.column_mut::<u32>().unwrap().push(42);
996        arch.push_entity(e0);
997
998        let (removed, moved) = arch.swap_remove_entity(0);
999        assert_eq!(removed, e0);
1000        assert!(moved.is_none());
1001        assert!(arch.is_empty());
1002    }
1003
1004    #[test]
1005    fn archetype_column_len_matches_entity_count() {
1006        let mut store = ArchetypeStore::new();
1007        store.register_column::<u32>();
1008        store.register_column::<bool>();
1009
1010        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<bool>()]);
1011        let arch_id = store.get_or_create(layout);
1012
1013        let arch = store.get_mut(arch_id);
1014        for i in 0..5 {
1015            arch.column_mut::<u32>().unwrap().push(i);
1016            arch.column_mut::<bool>().unwrap().push(i % 2 == 0);
1017            arch.push_entity(Entity {
1018                index: i,
1019                generation: 0,
1020            });
1021        }
1022
1023        assert_eq!(arch.len(), 5);
1024        assert_eq!(arch.column::<u32>().unwrap().len(), 5);
1025        assert_eq!(arch.column::<bool>().unwrap().len(), 5);
1026    }
1027
1028    #[test]
1029    fn archetype_store_multiple_layouts() {
1030        let mut store = ArchetypeStore::new();
1031        store.register_column::<u32>();
1032        store.register_column::<f64>();
1033        store.register_column::<bool>();
1034
1035        let l1 = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1036        let l2 = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<f64>()]);
1037        let l3 = ArchetypeLayout::from_type_ids(&[
1038            TypeId::of::<u32>(),
1039            TypeId::of::<f64>(),
1040            TypeId::of::<bool>(),
1041        ]);
1042
1043        let id1 = store.get_or_create(l1);
1044        let id2 = store.get_or_create(l2);
1045        let id3 = store.get_or_create(l3);
1046
1047        assert_ne!(id1, id2);
1048        assert_ne!(id2, id3);
1049        assert_eq!(store.len(), 3);
1050    }
1051
1052    // ---- Edge cache -------------------------------------------------------
1053
1054    #[test]
1055    fn edge_cache_starts_empty() {
1056        let mut store = ArchetypeStore::new();
1057        store.register_column::<u32>();
1058
1059        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1060        let arch_id = store.get_or_create(layout);
1061        let arch = store.get(arch_id);
1062
1063        assert!(arch.edge(TypeId::of::<f64>()).is_none());
1064    }
1065
1066    #[test]
1067    fn edge_cache_set_and_get() {
1068        let mut store = ArchetypeStore::new();
1069        store.register_column::<u32>();
1070        store.register_column::<f64>();
1071
1072        let l1 = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1073        let l2 = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<f64>()]);
1074        let id1 = store.get_or_create(l1);
1075        let id2 = store.get_or_create(l2);
1076
1077        // Simulate: adding f64 to archetype 1 → archetype 2.
1078        store.get_mut(id1).set_edge(
1079            TypeId::of::<f64>(),
1080            ArchetypeEdge {
1081                add: Some(id2),
1082                remove: None,
1083            },
1084        );
1085
1086        let edge = store.get(id1).edge(TypeId::of::<f64>()).unwrap();
1087        assert_eq!(edge.add, Some(id2));
1088        assert_eq!(edge.remove, None);
1089    }
1090
1091    // ---- Column iter ------------------------------------------------------
1092
1093    #[test]
1094    fn column_iter() {
1095        let mut col = Column::<u32>::new();
1096        col.push(10);
1097        col.push(20);
1098        col.push(30);
1099        let vals: Vec<&u32> = col.iter().collect();
1100        assert_eq!(vals, vec![&10, &20, &30]);
1101    }
1102
1103    #[test]
1104    fn column_iter_mut() {
1105        let mut col = Column::<u32>::new();
1106        col.push(1);
1107        col.push(2);
1108        for val in col.iter_mut() {
1109            *val *= 10;
1110        }
1111        assert_eq!(*col.get(0).unwrap(), 10);
1112        assert_eq!(*col.get(1).unwrap(), 20);
1113    }
1114
1115    #[test]
1116    fn column_as_mut_ptr_points_to_first_element() {
1117        let mut col = Column::<u32>::new();
1118        col.push(7);
1119        col.push(9);
1120
1121        let ptr = col.as_mut_ptr();
1122
1123        // SAFETY: The column owns two initialized values.
1124        unsafe {
1125            assert_eq!(*ptr, 7);
1126            assert_eq!(*ptr.add(1), 9);
1127        }
1128    }
1129
1130    // ---- ArchetypeStore::get_two_mut --------------------------------------
1131
1132    #[test]
1133    fn archetype_store_get_two_mut() {
1134        let mut store = ArchetypeStore::new();
1135        store.register_column::<u32>();
1136        store.register_column::<f64>();
1137
1138        let l1 = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1139        let l2 = ArchetypeLayout::from_type_ids(&[TypeId::of::<f64>()]);
1140        let id1 = store.get_or_create(l1);
1141        let id2 = store.get_or_create(l2);
1142
1143        let (a1, a2) = store.get_two_mut(id1, id2);
1144        assert_eq!(a1.id(), id1);
1145        assert_eq!(a2.id(), id2);
1146    }
1147
1148    #[test]
1149    #[should_panic(expected = "get_two_mut called with same ArchetypeId")]
1150    fn archetype_store_get_two_mut_same_panics() {
1151        let mut store = ArchetypeStore::new();
1152        store.register_column::<u32>();
1153        let l = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1154        let id = store.get_or_create(l);
1155        store.get_two_mut(id, id);
1156    }
1157
1158    // ---- Archetype split-borrow helpers -----------------------------------
1159
1160    #[test]
1161    fn archetype_swap_remove_entity_entry() {
1162        let mut store = ArchetypeStore::new();
1163        store.register_column::<u32>();
1164        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1165        let arch_id = store.get_or_create(layout);
1166        let arch = store.get_mut(arch_id);
1167
1168        let e0 = Entity {
1169            index: 0,
1170            generation: 0,
1171        };
1172        let e1 = Entity {
1173            index: 1,
1174            generation: 0,
1175        };
1176
1177        arch.column_mut::<u32>().unwrap().push(10);
1178        arch.push_entity(e0);
1179        arch.column_mut::<u32>().unwrap().push(20);
1180        arch.push_entity(e1);
1181
1182        // Manually remove column data first
1183        arch.column_mut::<u32>().unwrap().swap_remove(0);
1184        // Then remove entity entry
1185        let (removed, moved) = arch.swap_remove_entity_entry(0);
1186        assert_eq!(removed, e0);
1187        assert_eq!(moved, Some(e1));
1188        assert_eq!(arch.len(), 1);
1189    }
1190
1191    #[test]
1192    fn archetype_entities_and_column_mut() {
1193        let mut store = ArchetypeStore::new();
1194        store.register_column::<u32>();
1195        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>()]);
1196        let arch_id = store.get_or_create(layout);
1197        let arch = store.get_mut(arch_id);
1198
1199        let e0 = Entity {
1200            index: 0,
1201            generation: 0,
1202        };
1203        arch.column_mut::<u32>().unwrap().push(42);
1204        arch.push_entity(e0);
1205
1206        {
1207            let (entities, col) = arch.entities_and_column_mut::<u32>().unwrap();
1208            assert_eq!(entities.len(), 1);
1209            assert_eq!(entities[0], e0);
1210            *col.get_mut(0).unwrap() = 99;
1211        } // mutable borrow ends here
1212        assert_eq!(*arch.column::<u32>().unwrap().get(0).unwrap(), 99);
1213    }
1214
1215    #[test]
1216    fn archetype_entities_and_two_columns_mut() {
1217        let mut store = ArchetypeStore::new();
1218        store.register_column::<u32>();
1219        store.register_column::<f64>();
1220        let layout = ArchetypeLayout::from_type_ids(&[TypeId::of::<u32>(), TypeId::of::<f64>()]);
1221        let arch_id = store.get_or_create(layout);
1222        let arch = store.get_mut(arch_id);
1223
1224        let e0 = Entity {
1225            index: 0,
1226            generation: 0,
1227        };
1228        arch.column_mut::<u32>().unwrap().push(10);
1229        arch.column_mut::<f64>().unwrap().push(1.5);
1230        arch.push_entity(e0);
1231
1232        {
1233            let (entities, col_u, col_f) = arch.entities_and_two_columns_mut::<u32, f64>().unwrap();
1234            assert_eq!(entities[0], e0);
1235            *col_u.get_mut(0).unwrap() = 20;
1236            *col_f.get_mut(0).unwrap() = 2.5;
1237        } // mutable borrow ends here
1238        assert_eq!(*arch.column::<u32>().unwrap().get(0).unwrap(), 20);
1239        assert_eq!(*arch.column::<f64>().unwrap().get(0).unwrap(), 2.5);
1240    }
1241
1242    #[test]
1243    fn archetype_entities_and_three_columns_mut() {
1244        let mut store = ArchetypeStore::new();
1245        store.register_column::<u32>();
1246        store.register_column::<f64>();
1247        store.register_column::<bool>();
1248        let layout = ArchetypeLayout::from_type_ids(&[
1249            TypeId::of::<u32>(),
1250            TypeId::of::<f64>(),
1251            TypeId::of::<bool>(),
1252        ]);
1253        let arch_id = store.get_or_create(layout);
1254        let arch = store.get_mut(arch_id);
1255
1256        let e0 = Entity {
1257            index: 0,
1258            generation: 0,
1259        };
1260        arch.column_mut::<u32>().unwrap().push(10);
1261        arch.column_mut::<f64>().unwrap().push(1.5);
1262        arch.column_mut::<bool>().unwrap().push(true);
1263        arch.push_entity(e0);
1264
1265        {
1266            let (entities, col_u, col_f, col_b) = arch
1267                .entities_and_three_columns_mut::<u32, f64, bool>()
1268                .unwrap();
1269            assert_eq!(entities[0], e0);
1270            *col_u.get_mut(0).unwrap() = 20;
1271            *col_f.get_mut(0).unwrap() = 2.5;
1272            *col_b.get_mut(0).unwrap() = false;
1273        }
1274
1275        assert_eq!(*arch.column::<u32>().unwrap().get(0).unwrap(), 20);
1276        assert_eq!(*arch.column::<f64>().unwrap().get(0).unwrap(), 2.5);
1277        assert!(!arch.column::<bool>().unwrap().get(0).unwrap());
1278    }
1279
1280    // ---- Column tick tracking ------------------------------------------------
1281
1282    #[test]
1283    fn column_push_defaults_to_sentinel_ticks() {
1284        let mut col = Column::<u32>::new();
1285        col.push(10);
1286        col.push(20);
1287        assert_eq!(col.added_tick(0), 0);
1288        assert_eq!(col.changed_tick(0), 0);
1289        assert_eq!(col.added_tick(1), 0);
1290        assert_eq!(col.changed_tick(1), 0);
1291    }
1292
1293    #[test]
1294    fn column_push_with_ticks() {
1295        let mut col = Column::<u32>::new();
1296        col.push_with_ticks(10, 5, 7);
1297        col.push_with_ticks(20, 8, 9);
1298        assert_eq!(col.added_tick(0), 5);
1299        assert_eq!(col.changed_tick(0), 7);
1300        assert_eq!(col.added_tick(1), 8);
1301        assert_eq!(col.changed_tick(1), 9);
1302    }
1303
1304    #[test]
1305    fn column_set_ticks() {
1306        let mut col = Column::<u32>::new();
1307        col.push(10);
1308        col.set_added_tick(0, 3);
1309        col.set_changed_tick(0, 5);
1310        assert_eq!(col.added_tick(0), 3);
1311        assert_eq!(col.changed_tick(0), 5);
1312    }
1313
1314    #[test]
1315    fn column_swap_remove_keeps_ticks_in_sync() {
1316        let mut col = Column::<u32>::new();
1317        col.push_with_ticks(10, 1, 2);
1318        col.push_with_ticks(20, 3, 4);
1319        col.push_with_ticks(30, 5, 6);
1320
1321        // Remove row 0 — row 2 (value=30) swaps into row 0.
1322        let removed = col.swap_remove(0);
1323        assert_eq!(removed, 10);
1324        assert_eq!(col.len(), 2);
1325        // Row 0 now holds the old row 2's data and ticks.
1326        assert_eq!(*col.get(0).unwrap(), 30);
1327        assert_eq!(col.added_tick(0), 5);
1328        assert_eq!(col.changed_tick(0), 6);
1329        // Row 1 is unchanged.
1330        assert_eq!(*col.get(1).unwrap(), 20);
1331        assert_eq!(col.added_tick(1), 3);
1332        assert_eq!(col.changed_tick(1), 4);
1333    }
1334
1335    #[test]
1336    fn column_move_to_transfers_ticks() {
1337        let mut src = Column::<u32>::new();
1338        src.push_with_ticks(10, 1, 2);
1339        src.push_with_ticks(20, 3, 4);
1340
1341        let mut dst = Column::<u32>::new();
1342
1343        // Move row 0 from src to dst (AnyColumn trait method).
1344        AnyColumn::move_to(&mut src, 0, &mut dst);
1345
1346        // src: row 0 was swap-removed, row 1 (20) moved to row 0.
1347        assert_eq!(src.len(), 1);
1348        assert_eq!(*src.get(0).unwrap(), 20);
1349        assert_eq!(src.added_tick(0), 3);
1350        assert_eq!(src.changed_tick(0), 4);
1351
1352        // dst: received value 10 with its original ticks.
1353        assert_eq!(dst.len(), 1);
1354        assert_eq!(*dst.get(0).unwrap(), 10);
1355        assert_eq!(dst.added_tick(0), 1);
1356        assert_eq!(dst.changed_tick(0), 2);
1357    }
1358
1359    #[test]
1360    fn column_swap_remove_and_drop_keeps_ticks_in_sync() {
1361        let mut col = Column::<u32>::new();
1362        col.push_with_ticks(10, 1, 2);
1363        col.push_with_ticks(20, 3, 4);
1364
1365        AnyColumn::swap_remove_and_drop(&mut col, 0);
1366        assert_eq!(col.len(), 1);
1367        assert_eq!(*col.get(0).unwrap(), 20);
1368        assert_eq!(col.added_tick(0), 3);
1369        assert_eq!(col.changed_tick(0), 4);
1370    }
1371
1372    #[test]
1373    fn column_tick_slices() {
1374        let mut col = Column::<u32>::new();
1375        col.push_with_ticks(10, 1, 2);
1376        col.push_with_ticks(20, 3, 4);
1377        assert_eq!(col.added_ticks(), &[1, 3]);
1378        assert_eq!(col.changed_ticks(), &[2, 4]);
1379    }
1380}