shipyard/iter/
into_shiperator.rs

1mod not;
2mod or;
3mod tracking;
4
5use crate::component::Component;
6use crate::entity_id::EntityId;
7#[cfg(feature = "parallel")]
8use crate::iter::ParShiperator;
9use crate::iter::{captain::ShiperatorCaptain, mixed::Mixed, Shiperator};
10use crate::optional::Optional;
11use crate::sparse_set::{FullRawWindow, FullRawWindowMut, RawEntityIdAccess};
12use crate::storage::StorageId;
13use crate::tracking::Tracking;
14use crate::views::{View, ViewMut};
15use crate::ShipHashSet;
16use alloc::vec::Vec;
17use core::ptr::NonNull;
18
19/// Creates view iterators.
20///
21/// `std::iter::IntoIterator` can't be used directly because of conflicting implementation.\
22/// This trait serves as substitute.
23pub trait IntoIter: IntoShiperator {
24    /// ### Example
25    /// ```
26    /// use shipyard::{Component, EntitiesViewMut, IntoIter, ViewMut, World};
27    ///
28    /// #[derive(Component, Clone, Copy)]
29    /// struct U32(u32);
30    ///
31    /// #[derive(Component)]
32    /// struct USIZE(usize);
33    ///
34    /// let world = World::new();
35    ///
36    /// let (mut entities, mut usizes, mut u32s) = world.borrow::<(EntitiesViewMut, ViewMut<USIZE>, ViewMut<U32>)>().unwrap();
37    ///
38    /// entities.add_entity((&mut usizes, &mut u32s), (USIZE(0), U32(1)));
39    /// entities.add_entity((&mut usizes, &mut u32s), (USIZE(2), U32(3)));
40    ///
41    /// (&mut usizes, &u32s).iter().for_each(|(mut x, &y)| {
42    ///     x.0 += y.0 as usize;
43    /// });
44    /// ```
45    fn iter(self) -> Shiperator<Self::Shiperator>;
46    /// ### Example
47    /// ```
48    /// use rayon::prelude::ParallelIterator;
49    /// use shipyard::{Component, EntitiesViewMut, IntoIter, ViewMut, World};
50    ///
51    /// #[derive(Component, Clone, Copy)]
52    /// struct U32(u32);
53    ///
54    /// #[derive(Component)]
55    /// struct USIZE(usize);
56    ///
57    /// let world = World::new();
58    ///
59    /// let (mut entities, mut usizes, mut u32s) = world.borrow::<(EntitiesViewMut, ViewMut<USIZE>, ViewMut<U32>)>().unwrap();
60    ///
61    /// entities.add_entity((&mut usizes, &mut u32s), (USIZE(0), U32(1)));
62    /// entities.add_entity((&mut usizes, &mut u32s), (USIZE(2), U32(3)));
63    ///
64    /// (&mut usizes, &u32s).par_iter().for_each(|(mut x, &y)| {
65    ///     x.0 += y.0 as usize;
66    /// });
67    /// ```
68    #[cfg(feature = "parallel")]
69    #[cfg_attr(docsrs, doc(cfg(feature = "parallel")))]
70    fn par_iter(self) -> ParShiperator<Self::Shiperator>;
71}
72
73impl<T: IntoShiperator> IntoIter for T
74where
75    <T as IntoShiperator>::Shiperator: ShiperatorCaptain,
76{
77    #[inline]
78    fn iter(self) -> Shiperator<Self::Shiperator> {
79        let mut storage_ids = ShipHashSet::new();
80        let (shiperator, len, entities) = self.into_shiperator(&mut storage_ids);
81        let is_infallible = shiperator.is_exact_sized();
82
83        Shiperator {
84            shiperator,
85            is_exact_sized: is_infallible,
86            entities,
87            start: 0,
88            end: len,
89        }
90    }
91
92    #[cfg(feature = "parallel")]
93    #[inline]
94    fn par_iter(self) -> ParShiperator<Self::Shiperator> {
95        ParShiperator(self.iter())
96    }
97}
98
99/// Turns a view into a Shiperator.
100pub trait IntoShiperator {
101    #[allow(missing_docs)]
102    type Shiperator;
103
104    /// Returns the Shiperator, its maximum length and `RawEntityIdAccess`.
105    fn into_shiperator(
106        self,
107        storage_ids: &mut ShipHashSet<StorageId>,
108    ) -> (Self::Shiperator, usize, RawEntityIdAccess);
109    /// Returns `true` if the Shiperator can be a captain.
110    fn can_captain() -> bool;
111    /// Returns `true` if the Shiperator can be a sailor.
112    fn can_sailor() -> bool;
113}
114
115impl<'tmp, 'v: 'tmp, T: Component, Track: Tracking> IntoShiperator for &'tmp View<'v, T, Track> {
116    type Shiperator = FullRawWindow<'tmp, T>;
117
118    #[inline]
119    fn into_shiperator(
120        self,
121        _storage_ids: &mut ShipHashSet<StorageId>,
122    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
123        let window = FullRawWindow::from_view(self);
124        let len = window.len();
125        let iter = window.entity_iter();
126
127        (window, len, iter)
128    }
129
130    #[inline]
131    fn can_captain() -> bool {
132        true
133    }
134
135    #[inline]
136    fn can_sailor() -> bool {
137        true
138    }
139}
140
141impl<'tmp, 'v: 'tmp, T: Component, Track: Tracking> IntoShiperator for &'tmp ViewMut<'v, T, Track> {
142    type Shiperator = FullRawWindow<'tmp, T>;
143
144    #[inline]
145    fn into_shiperator(
146        self,
147        _storage_ids: &mut ShipHashSet<StorageId>,
148    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
149        let window = FullRawWindow::from_view_mut(self);
150        let len = window.len();
151        let iter = window.entity_iter();
152
153        (window, len, iter)
154    }
155
156    #[inline]
157    fn can_captain() -> bool {
158        true
159    }
160
161    #[inline]
162    fn can_sailor() -> bool {
163        true
164    }
165}
166
167impl<'tmp, 'v: 'tmp, T: Component, Track> IntoShiperator for &'tmp mut ViewMut<'v, T, Track> {
168    type Shiperator = FullRawWindowMut<'tmp, T, Track>;
169
170    #[inline]
171    fn into_shiperator(
172        self,
173        _storage_ids: &mut ShipHashSet<StorageId>,
174    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
175        let window = FullRawWindowMut::new(self);
176        let len = window.len();
177        let iter = window.entity_iter();
178
179        (window, len, iter)
180    }
181
182    #[inline]
183    fn can_captain() -> bool {
184        true
185    }
186
187    #[inline]
188    fn can_sailor() -> bool {
189        true
190    }
191}
192
193impl<'tmp> IntoShiperator for &'tmp [EntityId] {
194    type Shiperator = &'tmp [EntityId];
195
196    #[inline]
197    fn into_shiperator(
198        self,
199        _storage_ids: &mut ShipHashSet<StorageId>,
200    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
201        let len = self.len();
202        let iter =
203            RawEntityIdAccess::new(NonNull::new(self.as_ptr().cast_mut()).unwrap(), Vec::new());
204
205        (self, len, iter)
206    }
207
208    #[inline]
209    fn can_captain() -> bool {
210        true
211    }
212
213    #[inline]
214    fn can_sailor() -> bool {
215        false
216    }
217}
218
219impl<'tmp, 'v: 'tmp, T: Component> IntoShiperator for Optional<&'tmp View<'v, T>> {
220    type Shiperator = Optional<FullRawWindow<'tmp, T>>;
221
222    fn into_shiperator(
223        self,
224        storage_ids: &mut ShipHashSet<StorageId>,
225    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
226        let (shiperator, len, entities) = self.0.into_shiperator(storage_ids);
227
228        (Optional(shiperator), len, entities)
229    }
230
231    fn can_captain() -> bool {
232        false
233    }
234
235    fn can_sailor() -> bool {
236        true
237    }
238}
239
240impl<'tmp, 'v: 'tmp, T: Component, Track: Tracking> IntoShiperator
241    for Optional<&'tmp ViewMut<'v, T, Track>>
242{
243    type Shiperator = Optional<FullRawWindow<'tmp, T>>;
244
245    fn into_shiperator(
246        self,
247        storage_ids: &mut ShipHashSet<StorageId>,
248    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
249        let (shiperator, len, entities) = self.0.into_shiperator(storage_ids);
250
251        (Optional(shiperator), len, entities)
252    }
253
254    fn can_captain() -> bool {
255        false
256    }
257
258    fn can_sailor() -> bool {
259        true
260    }
261}
262
263impl<'tmp, 'v: 'tmp, T: Component, Track: Tracking> IntoShiperator
264    for Optional<&'tmp mut ViewMut<'v, T, Track>>
265{
266    type Shiperator = Optional<FullRawWindowMut<'tmp, T, Track>>;
267
268    fn into_shiperator(
269        self,
270        storage_ids: &mut ShipHashSet<StorageId>,
271    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
272        let (shiperator, len, entities) = self.0.into_shiperator(storage_ids);
273
274        (Optional(shiperator), len, entities)
275    }
276
277    fn can_captain() -> bool {
278        false
279    }
280
281    fn can_sailor() -> bool {
282        true
283    }
284}
285
286impl<T: IntoShiperator> IntoShiperator for (T,) {
287    type Shiperator = T::Shiperator;
288
289    fn into_shiperator(
290        self,
291        storage_ids: &mut ShipHashSet<StorageId>,
292    ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
293        self.0.into_shiperator(storage_ids)
294    }
295
296    fn can_captain() -> bool {
297        T::can_captain()
298    }
299
300    fn can_sailor() -> bool {
301        T::can_sailor()
302    }
303}
304
305// It's not currently possible to have a final non repeating '+'
306// https://github.com/rust-lang/rust/issues/18700
307macro_rules! strip_plus {
308    (+ $($rest: tt)*) => {
309        $($rest)*
310    }
311}
312
313// This is used in mixed.rs
314pub(crate) use strip_plus;
315
316macro_rules! impl_into_shiperator_tuple {
317    ($(($type: ident, $index: tt))+) => {
318        impl<$($type: IntoShiperator),+> IntoShiperator for ($($type,)+) where $(<$type as IntoShiperator>::Shiperator: ShiperatorCaptain),+ {
319            type Shiperator = Mixed<($($type::Shiperator,)+)>;
320
321            #[inline]
322            #[track_caller]
323            fn into_shiperator(
324                self,
325                storage_ids: &mut ShipHashSet<StorageId>,
326            ) -> (Self::Shiperator, usize, RawEntityIdAccess) {
327                let mut shiperators = ($(self.$index.into_shiperator(storage_ids),)+);
328
329                let can_captains = ($(
330                    $type::can_captain(),
331                )+);
332
333                let can_any_captain = $(
334                    can_captains.$index
335                )||+;
336
337                if !can_any_captain {
338                    panic!("Unable to build a Shiperator: None of the views could be a Captain.")
339                }
340
341                let can_sailors = ($(
342                    $type::can_sailor(),
343                )+);
344
345                $(
346                    if !can_captains.$index && !can_sailors.$index {
347                        panic!("Unable to build a Shiperator: View at index {} could neither be a Captain nor a Sailor.", $index)
348                    }
349                )+
350
351                let unable_sailor = strip_plus!($(
352                    + (!can_sailors.$index as usize)
353                )+);
354
355                if unable_sailor > 1 {
356                    panic!("Unable to build a Shiperator: Multiple views were unable to be Sailors.")
357                }
358
359                let sail_times = ($(
360                    shiperators.$index.0.sail_time(),
361                )+);
362
363                if unable_sailor == 1 {
364                    let mut mask = 0;
365                    let mut len = 0;
366                    let  mut entity_iter = RawEntityIdAccess::dangling();
367
368                    for (index, (can_sailor, shiperator_len, shiperator_entity_iter)) in
369                        [$((can_sailors.$index, shiperators.$index.1, shiperators.$index.2)),+]
370                            .into_iter()
371                            .enumerate()
372                    {
373                        if !can_sailor {
374                            mask = 1 << index;
375                            len = shiperator_len;
376                            entity_iter = shiperator_entity_iter;
377
378                            break;
379                        }
380                    }
381
382                    $(
383                        if mask & (1 << $index) == 0 {
384                            shiperators.$index.0.unpick();
385                        } else {
386                            if !shiperators.$index.0.is_exact_sized() {
387                                mask = 0;
388                            }
389                        }
390                    )+
391
392                    return (
393                        Mixed {
394                            shiperator: ($(shiperators.$index.0,)+),
395                            mask
396                        },
397                        len,
398                        entity_iter,
399                    );
400                }
401
402                let mut mask = 0;
403                let mut len = 0;
404                let mut entity_iter = RawEntityIdAccess::dangling();
405                let mut min_sail_time = usize::MAX;
406
407                $(
408                    if can_captains.$index && sail_times.$index < min_sail_time {
409                        mask = 1 << $index;
410                        len = shiperators.$index.1;
411                        entity_iter = shiperators.$index.2;
412                        min_sail_time = sail_times.$index;
413                    }
414                )+
415
416                let _ = min_sail_time;
417
418                $(
419                    if mask & (1 << $index) == 0 {
420                        shiperators.$index.0.unpick();
421                    } else {
422                        if !shiperators.$index.0.is_exact_sized() {
423                            mask = 0;
424                        }
425                    }
426                )+
427
428                (
429                    Mixed {
430                        shiperator: ($(shiperators.$index.0,)+),
431                        mask,
432                    },
433                    len,
434                    entity_iter,
435                )
436            }
437
438            #[inline]
439            fn can_captain() -> bool {
440                $(
441                    $type::can_captain()
442                )||+
443            }
444
445            #[inline]
446            fn can_sailor() -> bool {
447                $(
448                    $type::can_sailor()
449                )&&+
450            }
451        }
452    };
453}
454
455macro_rules! into_shiperator_tuple {
456    ($(($type: ident, $index: tt))+; ($type1: ident, $index1: tt) $(($queue_type: ident, $queue_index: tt))*) => {
457        impl_into_shiperator_tuple![$(($type, $index))*];
458        into_shiperator_tuple![$(($type, $index))* ($type1, $index1); $(($queue_type, $queue_index))*];
459    };
460    ($(($type: ident, $index: tt))+;) => {
461        impl_into_shiperator_tuple![$(($type, $index))*];
462    }
463}
464
465#[cfg(not(feature = "extended_tuple"))]
466into_shiperator_tuple![(A, 0) (B, 1); (C, 2) (D, 3) (E, 4) (F, 5) (G, 6) (H, 7) (I, 8) (J, 9)];
467#[cfg(feature = "extended_tuple")]
468into_shiperator_tuple![
469    (A, 0) (B, 1); (C, 2) (D, 3) (E, 4) (F, 5) (G, 6) (H, 7) (I, 8) (J, 9)
470    (K, 10) (L, 11) (M, 12) (N, 13) (O, 14) (P, 15) (Q, 16) (R, 17) (S, 18) (T, 19)
471    (U, 20) (V, 21) (W, 22) (X, 23) (Y, 24) (Z, 25) (AA, 26) (BB, 27) (CC, 28) (DD, 29)
472    (EE, 30) (FF, 31)
473];