• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlange-42 / ark / 13754430621

10 Mar 2025 12:52AM CUT coverage: 99.436% (+0.005%) from 99.431%
13754430621

Pull #175

github

web-flow
Merge 8bfc82421 into 5994e504b
Pull Request #175: Add `Query.Count`

95 of 96 new or added lines in 3 files covered. (98.96%)

9 existing lines in 1 file now uncovered.

6523 of 6560 relevant lines covered (99.44%)

26772.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.09
/ecs/query_gen.go
1
package ecs
2

3
// Code generated by go generate; DO NOT EDIT.
4

5
type cursor struct {
6
        archetype int
7
        table     int
8
        index     uintptr
9
        maxIndex  int64
10
}
11

12
type queryResult0 struct {
13
        Entity Entity
14
}
15

16
// Query0 is a query for 0 components.
17
// Use a [Filter0] to create one.
18
//
19
// Queries are one-time use iterators and must be re-created each time before iterating.
20
//
21
// See [Query2] for a usage example.
22
type Query0 struct {
23
        world      *World
24
        filter     *filter
25
        relations  []RelationID
26
        lock       uint8
27
        cursor     cursor
28
        tables     []tableID
29
        table      *table
30
        cache      *cacheEntry
31
        components []*componentStorage
32
}
33

34
func newQuery0(world *World, filter *filter, relations []RelationID,
35
        cacheID cacheID, components []*componentStorage) Query0 {
10✔
36
        var cache *cacheEntry
10✔
37
        if cacheID != maxCacheID {
11✔
38
                cache = world.storage.getRegisteredFilter(cacheID)
1✔
39
        }
1✔
40

41
        return Query0{
10✔
42
                world:      world,
10✔
43
                filter:     filter,
10✔
44
                relations:  relations,
10✔
45
                cache:      cache,
10✔
46
                lock:       world.lock(),
10✔
47
                components: components,
10✔
48
                cursor: cursor{
10✔
49
                        archetype: -1,
10✔
50
                        table:     -1,
10✔
51
                        index:     0,
10✔
52
                        maxIndex:  -1,
10✔
53
                },
10✔
54
        }
10✔
55
}
56

57
// Next advances the query's cursor to the next entity.
58
func (q *Query0) Next() bool {
239✔
59
        q.world.checkQueryNext(&q.cursor)
239✔
60
        if int64(q.cursor.index) < q.cursor.maxIndex {
452✔
61
                q.cursor.index++
213✔
62
                return true
213✔
63
        }
213✔
64
        return q.nextTableOrArchetype()
26✔
65
}
66

67
// Entity returns the current entity.
68
func (q *Query0) Entity() Entity {
218✔
69
        q.world.checkQueryGet(&q.cursor)
218✔
70
        return q.table.GetEntity(q.cursor.index)
218✔
71
}
218✔
72

73
// Count counts the entities matching this query.
74
//
75
// Has some overhead of iterating through archetypes.
76
// However, this is still much faster than manual counting via iteration.
77
//
78
// Does not iterate or close the query.
79
func (q *Query0) Count() int {
7✔
80
        if q.cache == nil {
13✔
81
                return countQuery(&q.world.storage, q.filter, q.relations)
6✔
82
        }
6✔
83
        return countQueryCache(&q.world.storage, q.cache, q.relations)
1✔
84
}
85

86
// Close closes the Query and unlocks the world.
87
//
88
// Automatically called when iteration completes.
89
// Needs to be called only if breaking out of the query iteration or not iterating at all.
90
func (q *Query0) Close() {
10✔
91
        q.cursor.archetype = -2
10✔
92
        q.cursor.table = -2
10✔
93
        q.tables = nil
10✔
94
        q.table = nil
10✔
95
        q.cache = nil
10✔
96
        q.world.unlock(q.lock)
10✔
97
}
10✔
98

99
func (q *Query0) nextTableOrArchetype() bool {
26✔
100
        if q.cache != nil {
31✔
101
                return q.nextTable(q.cache.tables)
5✔
102
        }
5✔
103
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
23✔
104
                return true
2✔
105
        }
2✔
106
        return q.nextArchetype()
19✔
107
}
108

109
func (q *Query0) nextArchetype() bool {
19✔
110
        maxArchIndex := len(q.world.storage.archetypes) - 1
19✔
111
        for q.cursor.archetype < maxArchIndex {
40✔
112
                q.cursor.archetype++
21✔
113
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
21✔
114
                if !q.filter.matches(&archetype.mask) {
25✔
115
                        continue
4✔
116
                }
117

118
                if !archetype.HasRelations() {
31✔
119
                        table := &q.world.storage.tables[archetype.tables[0]]
15✔
120
                        if table.Len() > 0 {
23✔
121
                                q.setTable(0, table)
8✔
122
                                return true
8✔
123
                        }
8✔
124
                        continue
7✔
125
                }
126

127
                q.tables = archetype.GetTables(q.relations)
1✔
128
                q.cursor.table = -1
1✔
129
                if q.nextTable(q.tables) {
2✔
130
                        return true
1✔
131
                }
1✔
132
        }
133
        q.Close()
9✔
134
        return false
9✔
135
}
136

137
func (q *Query0) nextTable(tables []tableID) bool {
17✔
138
        maxTableIndex := len(tables) - 1
17✔
139
        for q.cursor.table < maxTableIndex {
24✔
140
                q.cursor.table++
7✔
141
                table := &q.world.storage.tables[tables[q.cursor.table]]
7✔
142
                if table.Len() == 0 {
7✔
UNCOV
143
                        continue
×
144
                }
145
                if !table.Matches(q.relations) {
7✔
UNCOV
146
                        continue
×
147
                }
148
                q.setTable(q.cursor.table, table)
7✔
149
                return true
7✔
150
        }
151
        if q.cache != nil {
11✔
152
                q.Close()
1✔
153
        }
1✔
154
        return false
10✔
155
}
156

157
func (q *Query0) setTable(index int, table *table) {
15✔
158
        q.cursor.table = index
15✔
159
        q.table = table
15✔
160
        q.cursor.index = 0
15✔
161
        q.cursor.maxIndex = int64(q.table.Len() - 1)
15✔
162
}
15✔
163

164
type queryResult1[A any] struct {
165
        Entity Entity
166
        A      *A
167
}
168

169
// Query1 is a query for 1 components.
170
// Use a [Filter1] to create one.
171
//
172
// Queries are one-time use iterators and must be re-created each time before iterating.
173
//
174
// See [Query2] for a usage example.
175
type Query1[A any] struct {
176
        world      *World
177
        filter     *filter
178
        relations  []RelationID
179
        lock       uint8
180
        cursor     cursor
181
        tables     []tableID
182
        table      *table
183
        cache      *cacheEntry
184
        components []*componentStorage
185
        columnA    *column
186
}
187

188
func newQuery1[A any](world *World, filter *filter, ids []ID, relations []RelationID,
189
        cacheID cacheID, components []*componentStorage) Query1[A] {
1,118✔
190
        var cache *cacheEntry
1,118✔
191
        if cacheID != maxCacheID {
1,121✔
192
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
193
        }
3✔
194

195
        return Query1[A]{
1,118✔
196
                world:      world,
1,118✔
197
                filter:     filter,
1,118✔
198
                relations:  relations,
1,118✔
199
                cache:      cache,
1,118✔
200
                lock:       world.lock(),
1,118✔
201
                components: components,
1,118✔
202
                cursor: cursor{
1,118✔
203
                        archetype: -1,
1,118✔
204
                        table:     -1,
1,118✔
205
                        index:     0,
1,118✔
206
                        maxIndex:  -1,
1,118✔
207
                },
1,118✔
208
        }
1,118✔
209
}
210

211
// Next advances the query's cursor to the next entity.
212
func (q *Query1[A]) Next() bool {
998,916✔
213
        q.world.checkQueryNext(&q.cursor)
998,916✔
214
        if int64(q.cursor.index) < q.cursor.maxIndex {
1,995,586✔
215
                q.cursor.index++
996,670✔
216
                return true
996,670✔
217
        }
996,670✔
218
        return q.nextTableOrArchetype()
2,246✔
219
}
220

221
// Entity returns the current entity.
222
func (q *Query1[A]) Entity() Entity {
1,993,688✔
223
        q.world.checkQueryGet(&q.cursor)
1,993,688✔
224
        return q.table.GetEntity(q.cursor.index)
1,993,688✔
225
}
1,993,688✔
226

227
// Get returns the queried components of the current entity.
228
//
229
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
230
func (q *Query1[A]) Get() *A {
996,981✔
231
        q.world.checkQueryGet(&q.cursor)
996,981✔
232
        return (*A)(q.columnA.Get(q.cursor.index))
996,981✔
233
}
996,981✔
234

235
// GetRelation returns the entity relation target of the component at the given index.
236
func (q *Query1[A]) GetRelation(index int) Entity {
42✔
237
        return q.components[index].columns[q.table.id].target
42✔
238
}
42✔
239

240
// Count counts the entities matching this query.
241
//
242
// Has some overhead of iterating through archetypes.
243
// However, this is still much faster than manual counting via iteration.
244
//
245
// Does not iterate or close the query.
246
func (q *Query1[A]) Count() int {
11✔
247
        if q.cache == nil {
19✔
248
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
249
        }
8✔
250
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
251
}
252

253
// Close closes the Query and unlocks the world.
254
//
255
// Automatically called when iteration completes.
256
// Needs to be called only if breaking out of the query iteration or not iterating at all.
257
func (q *Query1[A]) Close() {
1,118✔
258
        q.cursor.archetype = -2
1,118✔
259
        q.cursor.table = -2
1,118✔
260
        q.tables = nil
1,118✔
261
        q.table = nil
1,118✔
262
        q.cache = nil
1,118✔
263
        q.columnA = nil
1,118✔
264
        q.world.unlock(q.lock)
1,118✔
265
}
1,118✔
266

267
func (q *Query1[A]) nextTableOrArchetype() bool {
2,246✔
268
        if q.cache != nil {
2,253✔
269
                return q.nextTable(q.cache.tables)
7✔
270
        }
7✔
271
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
2,240✔
272
                return true
1✔
273
        }
1✔
274
        return q.nextArchetype()
2,238✔
275
}
276

277
func (q *Query1[A]) nextArchetype() bool {
2,238✔
278
        maxArchIndex := len(q.world.storage.archetypes) - 1
2,238✔
279
        for q.cursor.archetype < maxArchIndex {
4,753✔
280
                q.cursor.archetype++
2,515✔
281
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
2,515✔
282
                if !q.filter.matches(&archetype.mask) {
3,830✔
283
                        continue
1,315✔
284
                }
285

286
                if !archetype.HasRelations() {
2,376✔
287
                        table := &q.world.storage.tables[archetype.tables[0]]
1,177✔
288
                        if table.Len() > 0 {
2,286✔
289
                                q.setTable(0, table)
1,109✔
290
                                return true
1,109✔
291
                        }
1,109✔
292
                        continue
68✔
293
                }
294

295
                q.tables = archetype.GetTables(q.relations)
22✔
296
                q.cursor.table = -1
22✔
297
                if q.nextTable(q.tables) {
35✔
298
                        return true
13✔
299
                }
13✔
300
        }
301
        q.Close()
1,115✔
302
        return false
1,115✔
303
}
304

305
func (q *Query1[A]) nextTable(tables []tableID) bool {
1,152✔
306
        maxTableIndex := len(tables) - 1
1,152✔
307
        for q.cursor.table < maxTableIndex {
1,183✔
308
                q.cursor.table++
31✔
309
                table := &q.world.storage.tables[tables[q.cursor.table]]
31✔
310
                if table.Len() == 0 {
43✔
311
                        continue
12✔
312
                }
313
                if !table.Matches(q.relations) {
20✔
314
                        continue
1✔
315
                }
316
                q.setTable(q.cursor.table, table)
18✔
317
                return true
18✔
318
        }
319
        if q.cache != nil {
1,137✔
320
                q.Close()
3✔
321
        }
3✔
322
        return false
1,134✔
323
}
324

325
func (q *Query1[A]) setTable(index int, table *table) {
1,127✔
326
        q.cursor.table = index
1,127✔
327
        q.table = table
1,127✔
328
        q.columnA = q.components[0].columns[q.table.id]
1,127✔
329
        q.cursor.index = 0
1,127✔
330
        q.cursor.maxIndex = int64(q.table.Len() - 1)
1,127✔
331
}
1,127✔
332

333
type queryResult2[A any, B any] struct {
334
        Entity Entity
335
        A      *A
336
        B      *B
337
}
338

339
// Query2 is a query for 2 components.
340
// Use a [Filter2] to create one.
341
//
342
// Queries are one-time use iterators and must be re-created each time before iterating.
343
type Query2[A any, B any] struct {
344
        world      *World
345
        filter     *filter
346
        relations  []RelationID
347
        lock       uint8
348
        cursor     cursor
349
        tables     []tableID
350
        table      *table
351
        cache      *cacheEntry
352
        components []*componentStorage
353
        columnA    *column
354
        columnB    *column
355
}
356

357
func newQuery2[A any, B any](world *World, filter *filter, ids []ID, relations []RelationID,
358
        cacheID cacheID, components []*componentStorage) Query2[A, B] {
16✔
359
        var cache *cacheEntry
16✔
360
        if cacheID != maxCacheID {
19✔
361
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
362
        }
3✔
363

364
        return Query2[A, B]{
16✔
365
                world:      world,
16✔
366
                filter:     filter,
16✔
367
                relations:  relations,
16✔
368
                cache:      cache,
16✔
369
                lock:       world.lock(),
16✔
370
                components: components,
16✔
371
                cursor: cursor{
16✔
372
                        archetype: -1,
16✔
373
                        table:     -1,
16✔
374
                        index:     0,
16✔
375
                        maxIndex:  -1,
16✔
376
                },
16✔
377
        }
16✔
378
}
379

380
// Next advances the query's cursor to the next entity.
381
func (q *Query2[A, B]) Next() bool {
251✔
382
        q.world.checkQueryNext(&q.cursor)
251✔
383
        if int64(q.cursor.index) < q.cursor.maxIndex {
469✔
384
                q.cursor.index++
218✔
385
                return true
218✔
386
        }
218✔
387
        return q.nextTableOrArchetype()
33✔
388
}
389

390
// Entity returns the current entity.
391
func (q *Query2[A, B]) Entity() Entity {
204✔
392
        q.world.checkQueryGet(&q.cursor)
204✔
393
        return q.table.GetEntity(q.cursor.index)
204✔
394
}
204✔
395

396
// Get returns the queried components of the current entity.
397
//
398
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
399
func (q *Query2[A, B]) Get() (*A, *B) {
236✔
400
        q.world.checkQueryGet(&q.cursor)
236✔
401
        return (*A)(q.columnA.Get(q.cursor.index)),
236✔
402
                (*B)(q.columnB.Get(q.cursor.index))
236✔
403
}
236✔
404

405
// GetRelation returns the entity relation target of the component at the given index.
406
func (q *Query2[A, B]) GetRelation(index int) Entity {
40✔
407
        return q.components[index].columns[q.table.id].target
40✔
408
}
40✔
409

410
// Count counts the entities matching this query.
411
//
412
// Has some overhead of iterating through archetypes.
413
// However, this is still much faster than manual counting via iteration.
414
//
415
// Does not iterate or close the query.
416
func (q *Query2[A, B]) Count() int {
11✔
417
        if q.cache == nil {
19✔
418
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
419
        }
8✔
420
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
421
}
422

423
// Close closes the Query and unlocks the world.
424
//
425
// Automatically called when iteration completes.
426
// Needs to be called only if breaking out of the query iteration or not iterating at all.
427
func (q *Query2[A, B]) Close() {
16✔
428
        q.cursor.archetype = -2
16✔
429
        q.cursor.table = -2
16✔
430
        q.tables = nil
16✔
431
        q.table = nil
16✔
432
        q.cache = nil
16✔
433
        q.columnA = nil
16✔
434
        q.columnB = nil
16✔
435
        q.world.unlock(q.lock)
16✔
436
}
16✔
437

438
func (q *Query2[A, B]) nextTableOrArchetype() bool {
33✔
439
        if q.cache != nil {
40✔
440
                return q.nextTable(q.cache.tables)
7✔
441
        }
7✔
442
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
27✔
443
                return true
1✔
444
        }
1✔
445
        return q.nextArchetype()
25✔
446
}
447

448
func (q *Query2[A, B]) nextArchetype() bool {
25✔
449
        maxArchIndex := len(q.world.storage.archetypes) - 1
25✔
450
        for q.cursor.archetype < maxArchIndex {
58✔
451
                q.cursor.archetype++
33✔
452
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
33✔
453
                if !q.filter.matches(&archetype.mask) {
54✔
454
                        continue
21✔
455
                }
456

457
                if !archetype.HasRelations() {
18✔
458
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
459
                        if table.Len() > 0 {
14✔
460
                                q.setTable(0, table)
7✔
461
                                return true
7✔
462
                        }
7✔
UNCOV
463
                        continue
×
464
                }
465

466
                q.tables = archetype.GetTables(q.relations)
4✔
467
                q.cursor.table = -1
4✔
468
                if q.nextTable(q.tables) {
8✔
469
                        return true
4✔
470
                }
4✔
471
        }
472
        q.Close()
13✔
473
        return false
13✔
474
}
475

476
func (q *Query2[A, B]) nextTable(tables []tableID) bool {
23✔
477
        maxTableIndex := len(tables) - 1
23✔
478
        for q.cursor.table < maxTableIndex {
36✔
479
                q.cursor.table++
13✔
480
                table := &q.world.storage.tables[tables[q.cursor.table]]
13✔
481
                if table.Len() == 0 {
16✔
482
                        continue
3✔
483
                }
484
                if !table.Matches(q.relations) {
11✔
485
                        continue
1✔
486
                }
487
                q.setTable(q.cursor.table, table)
9✔
488
                return true
9✔
489
        }
490
        if q.cache != nil {
17✔
491
                q.Close()
3✔
492
        }
3✔
493
        return false
14✔
494
}
495

496
func (q *Query2[A, B]) setTable(index int, table *table) {
16✔
497
        q.cursor.table = index
16✔
498
        q.table = table
16✔
499
        q.columnA = q.components[0].columns[q.table.id]
16✔
500
        q.columnB = q.components[1].columns[q.table.id]
16✔
501
        q.cursor.index = 0
16✔
502
        q.cursor.maxIndex = int64(q.table.Len() - 1)
16✔
503
}
16✔
504

505
type queryResult3[A any, B any, C any] struct {
506
        Entity Entity
507
        A      *A
508
        B      *B
509
        C      *C
510
}
511

512
// Query3 is a query for 3 components.
513
// Use a [Filter3] to create one.
514
//
515
// Queries are one-time use iterators and must be re-created each time before iterating.
516
//
517
// See [Query2] for a usage example.
518
type Query3[A any, B any, C any] struct {
519
        world      *World
520
        filter     *filter
521
        relations  []RelationID
522
        lock       uint8
523
        cursor     cursor
524
        tables     []tableID
525
        table      *table
526
        cache      *cacheEntry
527
        components []*componentStorage
528
        columnA    *column
529
        columnB    *column
530
        columnC    *column
531
}
532

533
func newQuery3[A any, B any, C any](world *World, filter *filter, ids []ID, relations []RelationID,
534
        cacheID cacheID, components []*componentStorage) Query3[A, B, C] {
16✔
535
        var cache *cacheEntry
16✔
536
        if cacheID != maxCacheID {
19✔
537
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
538
        }
3✔
539

540
        return Query3[A, B, C]{
16✔
541
                world:      world,
16✔
542
                filter:     filter,
16✔
543
                relations:  relations,
16✔
544
                cache:      cache,
16✔
545
                lock:       world.lock(),
16✔
546
                components: components,
16✔
547
                cursor: cursor{
16✔
548
                        archetype: -1,
16✔
549
                        table:     -1,
16✔
550
                        index:     0,
16✔
551
                        maxIndex:  -1,
16✔
552
                },
16✔
553
        }
16✔
554
}
555

556
// Next advances the query's cursor to the next entity.
557
func (q *Query3[A, B, C]) Next() bool {
289✔
558
        q.world.checkQueryNext(&q.cursor)
289✔
559
        if int64(q.cursor.index) < q.cursor.maxIndex {
539✔
560
                q.cursor.index++
250✔
561
                return true
250✔
562
        }
250✔
563
        return q.nextTableOrArchetype()
39✔
564
}
565

566
// Entity returns the current entity.
567
func (q *Query3[A, B, C]) Entity() Entity {
204✔
568
        q.world.checkQueryGet(&q.cursor)
204✔
569
        return q.table.GetEntity(q.cursor.index)
204✔
570
}
204✔
571

572
// Get returns the queried components of the current entity.
573
//
574
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
575
func (q *Query3[A, B, C]) Get() (*A, *B, *C) {
204✔
576
        q.world.checkQueryGet(&q.cursor)
204✔
577
        return (*A)(q.columnA.Get(q.cursor.index)),
204✔
578
                (*B)(q.columnB.Get(q.cursor.index)),
204✔
579
                (*C)(q.columnC.Get(q.cursor.index))
204✔
580
}
204✔
581

582
// GetRelation returns the entity relation target of the component at the given index.
583
func (q *Query3[A, B, C]) GetRelation(index int) Entity {
40✔
584
        return q.components[index].columns[q.table.id].target
40✔
585
}
40✔
586

587
// Count counts the entities matching this query.
588
//
589
// Has some overhead of iterating through archetypes.
590
// However, this is still much faster than manual counting via iteration.
591
//
592
// Does not iterate or close the query.
593
func (q *Query3[A, B, C]) Count() int {
11✔
594
        if q.cache == nil {
19✔
595
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
596
        }
8✔
597
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
598
}
599

600
// Close closes the Query and unlocks the world.
601
//
602
// Automatically called when iteration completes.
603
// Needs to be called only if breaking out of the query iteration or not iterating at all.
604
func (q *Query3[A, B, C]) Close() {
16✔
605
        q.cursor.archetype = -2
16✔
606
        q.cursor.table = -2
16✔
607
        q.tables = nil
16✔
608
        q.table = nil
16✔
609
        q.cache = nil
16✔
610
        q.columnA = nil
16✔
611
        q.columnB = nil
16✔
612
        q.columnC = nil
16✔
613
        q.world.unlock(q.lock)
16✔
614
}
16✔
615

616
func (q *Query3[A, B, C]) nextTableOrArchetype() bool {
39✔
617
        if q.cache != nil {
46✔
618
                return q.nextTable(q.cache.tables)
7✔
619
        }
7✔
620
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
37✔
621
                return true
5✔
622
        }
5✔
623
        return q.nextArchetype()
27✔
624
}
625

626
func (q *Query3[A, B, C]) nextArchetype() bool {
27✔
627
        maxArchIndex := len(q.world.storage.archetypes) - 1
27✔
628
        for q.cursor.archetype < maxArchIndex {
62✔
629
                q.cursor.archetype++
35✔
630
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
35✔
631
                if !q.filter.matches(&archetype.mask) {
56✔
632
                        continue
21✔
633
                }
634

635
                if !archetype.HasRelations() {
20✔
636
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
637
                        if table.Len() > 0 {
14✔
638
                                q.setTable(0, table)
7✔
639
                                return true
7✔
640
                        }
7✔
UNCOV
641
                        continue
×
642
                }
643

644
                q.tables = archetype.GetTables(q.relations)
6✔
645
                q.cursor.table = -1
6✔
646
                if q.nextTable(q.tables) {
12✔
647
                        return true
6✔
648
                }
6✔
649
        }
650
        q.Close()
13✔
651
        return false
13✔
652
}
653

654
func (q *Query3[A, B, C]) nextTable(tables []tableID) bool {
31✔
655
        maxTableIndex := len(tables) - 1
31✔
656
        for q.cursor.table < maxTableIndex {
51✔
657
                q.cursor.table++
20✔
658
                table := &q.world.storage.tables[tables[q.cursor.table]]
20✔
659
                if table.Len() == 0 {
23✔
660
                        continue
3✔
661
                }
662
                if !table.Matches(q.relations) {
19✔
663
                        continue
2✔
664
                }
665
                q.setTable(q.cursor.table, table)
15✔
666
                return true
15✔
667
        }
668
        if q.cache != nil {
19✔
669
                q.Close()
3✔
670
        }
3✔
671
        return false
16✔
672
}
673

674
func (q *Query3[A, B, C]) setTable(index int, table *table) {
22✔
675
        q.cursor.table = index
22✔
676
        q.table = table
22✔
677
        q.columnA = q.components[0].columns[q.table.id]
22✔
678
        q.columnB = q.components[1].columns[q.table.id]
22✔
679
        q.columnC = q.components[2].columns[q.table.id]
22✔
680
        q.cursor.index = 0
22✔
681
        q.cursor.maxIndex = int64(q.table.Len() - 1)
22✔
682
}
22✔
683

684
type queryResult4[A any, B any, C any, D any] struct {
685
        Entity Entity
686
        A      *A
687
        B      *B
688
        C      *C
689
        D      *D
690
}
691

692
// Query4 is a query for 4 components.
693
// Use a [Filter4] to create one.
694
//
695
// Queries are one-time use iterators and must be re-created each time before iterating.
696
//
697
// See [Query2] for a usage example.
698
type Query4[A any, B any, C any, D any] struct {
699
        world      *World
700
        filter     *filter
701
        relations  []RelationID
702
        lock       uint8
703
        cursor     cursor
704
        tables     []tableID
705
        table      *table
706
        cache      *cacheEntry
707
        components []*componentStorage
708
        columnA    *column
709
        columnB    *column
710
        columnC    *column
711
        columnD    *column
712
}
713

714
func newQuery4[A any, B any, C any, D any](world *World, filter *filter, ids []ID, relations []RelationID,
715
        cacheID cacheID, components []*componentStorage) Query4[A, B, C, D] {
13✔
716
        var cache *cacheEntry
13✔
717
        if cacheID != maxCacheID {
16✔
718
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
719
        }
3✔
720

721
        return Query4[A, B, C, D]{
13✔
722
                world:      world,
13✔
723
                filter:     filter,
13✔
724
                relations:  relations,
13✔
725
                cache:      cache,
13✔
726
                lock:       world.lock(),
13✔
727
                components: components,
13✔
728
                cursor: cursor{
13✔
729
                        archetype: -1,
13✔
730
                        table:     -1,
13✔
731
                        index:     0,
13✔
732
                        maxIndex:  -1,
13✔
733
                },
13✔
734
        }
13✔
735
}
736

737
// Next advances the query's cursor to the next entity.
738
func (q *Query4[A, B, C, D]) Next() bool {
216✔
739
        q.world.checkQueryNext(&q.cursor)
216✔
740
        if int64(q.cursor.index) < q.cursor.maxIndex {
403✔
741
                q.cursor.index++
187✔
742
                return true
187✔
743
        }
187✔
744
        return q.nextTableOrArchetype()
29✔
745
}
746

747
// Entity returns the current entity.
748
func (q *Query4[A, B, C, D]) Entity() Entity {
204✔
749
        q.world.checkQueryGet(&q.cursor)
204✔
750
        return q.table.GetEntity(q.cursor.index)
204✔
751
}
204✔
752

753
// Get returns the queried components of the current entity.
754
//
755
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
756
func (q *Query4[A, B, C, D]) Get() (*A, *B, *C, *D) {
204✔
757
        q.world.checkQueryGet(&q.cursor)
204✔
758
        return (*A)(q.columnA.Get(q.cursor.index)),
204✔
759
                (*B)(q.columnB.Get(q.cursor.index)),
204✔
760
                (*C)(q.columnC.Get(q.cursor.index)),
204✔
761
                (*D)(q.columnD.Get(q.cursor.index))
204✔
762
}
204✔
763

764
// GetRelation returns the entity relation target of the component at the given index.
765
func (q *Query4[A, B, C, D]) GetRelation(index int) Entity {
40✔
766
        return q.components[index].columns[q.table.id].target
40✔
767
}
40✔
768

769
// Count counts the entities matching this query.
770
//
771
// Has some overhead of iterating through archetypes.
772
// However, this is still much faster than manual counting via iteration.
773
//
774
// Does not iterate or close the query.
775
func (q *Query4[A, B, C, D]) Count() int {
11✔
776
        if q.cache == nil {
19✔
777
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
778
        }
8✔
779
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
780
}
781

782
// Close closes the Query and unlocks the world.
783
//
784
// Automatically called when iteration completes.
785
// Needs to be called only if breaking out of the query iteration or not iterating at all.
786
func (q *Query4[A, B, C, D]) Close() {
13✔
787
        q.cursor.archetype = -2
13✔
788
        q.cursor.table = -2
13✔
789
        q.tables = nil
13✔
790
        q.table = nil
13✔
791
        q.cache = nil
13✔
792
        q.columnA = nil
13✔
793
        q.columnB = nil
13✔
794
        q.columnC = nil
13✔
795
        q.columnD = nil
13✔
796
        q.world.unlock(q.lock)
13✔
797
}
13✔
798

799
func (q *Query4[A, B, C, D]) nextTableOrArchetype() bool {
29✔
800
        if q.cache != nil {
36✔
801
                return q.nextTable(q.cache.tables)
7✔
802
        }
7✔
803
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
23✔
804
                return true
1✔
805
        }
1✔
806
        return q.nextArchetype()
21✔
807
}
808

809
func (q *Query4[A, B, C, D]) nextArchetype() bool {
21✔
810
        maxArchIndex := len(q.world.storage.archetypes) - 1
21✔
811
        for q.cursor.archetype < maxArchIndex {
50✔
812
                q.cursor.archetype++
29✔
813
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
29✔
814
                if !q.filter.matches(&archetype.mask) {
47✔
815
                        continue
18✔
816
                }
817

818
                if !archetype.HasRelations() {
17✔
819
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
820
                        if table.Len() > 0 {
14✔
821
                                q.setTable(0, table)
7✔
822
                                return true
7✔
823
                        }
7✔
UNCOV
824
                        continue
×
825
                }
826

827
                q.tables = archetype.GetTables(q.relations)
3✔
828
                q.cursor.table = -1
3✔
829
                if q.nextTable(q.tables) {
6✔
830
                        return true
3✔
831
                }
3✔
832
        }
833
        q.Close()
10✔
834
        return false
10✔
835
}
836

837
func (q *Query4[A, B, C, D]) nextTable(tables []tableID) bool {
21✔
838
        maxTableIndex := len(tables) - 1
21✔
839
        for q.cursor.table < maxTableIndex {
33✔
840
                q.cursor.table++
12✔
841
                table := &q.world.storage.tables[tables[q.cursor.table]]
12✔
842
                if table.Len() == 0 {
15✔
843
                        continue
3✔
844
                }
845
                if !table.Matches(q.relations) {
10✔
846
                        continue
1✔
847
                }
848
                q.setTable(q.cursor.table, table)
8✔
849
                return true
8✔
850
        }
851
        if q.cache != nil {
16✔
852
                q.Close()
3✔
853
        }
3✔
854
        return false
13✔
855
}
856

857
func (q *Query4[A, B, C, D]) setTable(index int, table *table) {
15✔
858
        q.cursor.table = index
15✔
859
        q.table = table
15✔
860
        q.columnA = q.components[0].columns[q.table.id]
15✔
861
        q.columnB = q.components[1].columns[q.table.id]
15✔
862
        q.columnC = q.components[2].columns[q.table.id]
15✔
863
        q.columnD = q.components[3].columns[q.table.id]
15✔
864
        q.cursor.index = 0
15✔
865
        q.cursor.maxIndex = int64(q.table.Len() - 1)
15✔
866
}
15✔
867

868
type queryResult5[A any, B any, C any, D any, E any] struct {
869
        Entity Entity
870
        A      *A
871
        B      *B
872
        C      *C
873
        D      *D
874
        E      *E
875
}
876

877
// Query5 is a query for 5 components.
878
// Use a [Filter5] to create one.
879
//
880
// Queries are one-time use iterators and must be re-created each time before iterating.
881
//
882
// See [Query2] for a usage example.
883
type Query5[A any, B any, C any, D any, E any] struct {
884
        world      *World
885
        filter     *filter
886
        relations  []RelationID
887
        lock       uint8
888
        cursor     cursor
889
        tables     []tableID
890
        table      *table
891
        cache      *cacheEntry
892
        components []*componentStorage
893
        columnA    *column
894
        columnB    *column
895
        columnC    *column
896
        columnD    *column
897
        columnE    *column
898
}
899

900
func newQuery5[A any, B any, C any, D any, E any](world *World, filter *filter, ids []ID, relations []RelationID,
901
        cacheID cacheID, components []*componentStorage) Query5[A, B, C, D, E] {
13✔
902
        var cache *cacheEntry
13✔
903
        if cacheID != maxCacheID {
16✔
904
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
905
        }
3✔
906

907
        return Query5[A, B, C, D, E]{
13✔
908
                world:      world,
13✔
909
                filter:     filter,
13✔
910
                relations:  relations,
13✔
911
                cache:      cache,
13✔
912
                lock:       world.lock(),
13✔
913
                components: components,
13✔
914
                cursor: cursor{
13✔
915
                        archetype: -1,
13✔
916
                        table:     -1,
13✔
917
                        index:     0,
13✔
918
                        maxIndex:  -1,
13✔
919
                },
13✔
920
        }
13✔
921
}
922

923
// Next advances the query's cursor to the next entity.
924
func (q *Query5[A, B, C, D, E]) Next() bool {
216✔
925
        q.world.checkQueryNext(&q.cursor)
216✔
926
        if int64(q.cursor.index) < q.cursor.maxIndex {
403✔
927
                q.cursor.index++
187✔
928
                return true
187✔
929
        }
187✔
930
        return q.nextTableOrArchetype()
29✔
931
}
932

933
// Entity returns the current entity.
934
func (q *Query5[A, B, C, D, E]) Entity() Entity {
204✔
935
        q.world.checkQueryGet(&q.cursor)
204✔
936
        return q.table.GetEntity(q.cursor.index)
204✔
937
}
204✔
938

939
// Get returns the queried components of the current entity.
940
//
941
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
942
func (q *Query5[A, B, C, D, E]) Get() (*A, *B, *C, *D, *E) {
204✔
943
        q.world.checkQueryGet(&q.cursor)
204✔
944
        return (*A)(q.columnA.Get(q.cursor.index)),
204✔
945
                (*B)(q.columnB.Get(q.cursor.index)),
204✔
946
                (*C)(q.columnC.Get(q.cursor.index)),
204✔
947
                (*D)(q.columnD.Get(q.cursor.index)),
204✔
948
                (*E)(q.columnE.Get(q.cursor.index))
204✔
949
}
204✔
950

951
// GetRelation returns the entity relation target of the component at the given index.
952
func (q *Query5[A, B, C, D, E]) GetRelation(index int) Entity {
40✔
953
        return q.components[index].columns[q.table.id].target
40✔
954
}
40✔
955

956
// Count counts the entities matching this query.
957
//
958
// Has some overhead of iterating through archetypes.
959
// However, this is still much faster than manual counting via iteration.
960
//
961
// Does not iterate or close the query.
962
func (q *Query5[A, B, C, D, E]) Count() int {
11✔
963
        if q.cache == nil {
19✔
964
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
965
        }
8✔
966
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
967
}
968

969
// Close closes the Query and unlocks the world.
970
//
971
// Automatically called when iteration completes.
972
// Needs to be called only if breaking out of the query iteration or not iterating at all.
973
func (q *Query5[A, B, C, D, E]) Close() {
13✔
974
        q.cursor.archetype = -2
13✔
975
        q.cursor.table = -2
13✔
976
        q.tables = nil
13✔
977
        q.table = nil
13✔
978
        q.cache = nil
13✔
979
        q.columnA = nil
13✔
980
        q.columnB = nil
13✔
981
        q.columnC = nil
13✔
982
        q.columnD = nil
13✔
983
        q.columnE = nil
13✔
984
        q.world.unlock(q.lock)
13✔
985
}
13✔
986

987
func (q *Query5[A, B, C, D, E]) nextTableOrArchetype() bool {
29✔
988
        if q.cache != nil {
36✔
989
                return q.nextTable(q.cache.tables)
7✔
990
        }
7✔
991
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
23✔
992
                return true
1✔
993
        }
1✔
994
        return q.nextArchetype()
21✔
995
}
996

997
func (q *Query5[A, B, C, D, E]) nextArchetype() bool {
21✔
998
        maxArchIndex := len(q.world.storage.archetypes) - 1
21✔
999
        for q.cursor.archetype < maxArchIndex {
50✔
1000
                q.cursor.archetype++
29✔
1001
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
29✔
1002
                if !q.filter.matches(&archetype.mask) {
47✔
1003
                        continue
18✔
1004
                }
1005

1006
                if !archetype.HasRelations() {
17✔
1007
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
1008
                        if table.Len() > 0 {
14✔
1009
                                q.setTable(0, table)
7✔
1010
                                return true
7✔
1011
                        }
7✔
UNCOV
1012
                        continue
×
1013
                }
1014

1015
                q.tables = archetype.GetTables(q.relations)
3✔
1016
                q.cursor.table = -1
3✔
1017
                if q.nextTable(q.tables) {
6✔
1018
                        return true
3✔
1019
                }
3✔
1020
        }
1021
        q.Close()
10✔
1022
        return false
10✔
1023
}
1024

1025
func (q *Query5[A, B, C, D, E]) nextTable(tables []tableID) bool {
21✔
1026
        maxTableIndex := len(tables) - 1
21✔
1027
        for q.cursor.table < maxTableIndex {
33✔
1028
                q.cursor.table++
12✔
1029
                table := &q.world.storage.tables[tables[q.cursor.table]]
12✔
1030
                if table.Len() == 0 {
15✔
1031
                        continue
3✔
1032
                }
1033
                if !table.Matches(q.relations) {
10✔
1034
                        continue
1✔
1035
                }
1036
                q.setTable(q.cursor.table, table)
8✔
1037
                return true
8✔
1038
        }
1039
        if q.cache != nil {
16✔
1040
                q.Close()
3✔
1041
        }
3✔
1042
        return false
13✔
1043
}
1044

1045
func (q *Query5[A, B, C, D, E]) setTable(index int, table *table) {
15✔
1046
        q.cursor.table = index
15✔
1047
        q.table = table
15✔
1048
        q.columnA = q.components[0].columns[q.table.id]
15✔
1049
        q.columnB = q.components[1].columns[q.table.id]
15✔
1050
        q.columnC = q.components[2].columns[q.table.id]
15✔
1051
        q.columnD = q.components[3].columns[q.table.id]
15✔
1052
        q.columnE = q.components[4].columns[q.table.id]
15✔
1053
        q.cursor.index = 0
15✔
1054
        q.cursor.maxIndex = int64(q.table.Len() - 1)
15✔
1055
}
15✔
1056

1057
type queryResult6[A any, B any, C any, D any, E any, F any] struct {
1058
        Entity Entity
1059
        A      *A
1060
        B      *B
1061
        C      *C
1062
        D      *D
1063
        E      *E
1064
        F      *F
1065
}
1066

1067
// Query6 is a query for 6 components.
1068
// Use a [Filter6] to create one.
1069
//
1070
// Queries are one-time use iterators and must be re-created each time before iterating.
1071
//
1072
// See [Query2] for a usage example.
1073
type Query6[A any, B any, C any, D any, E any, F any] struct {
1074
        world      *World
1075
        filter     *filter
1076
        relations  []RelationID
1077
        lock       uint8
1078
        cursor     cursor
1079
        tables     []tableID
1080
        table      *table
1081
        cache      *cacheEntry
1082
        components []*componentStorage
1083
        columnA    *column
1084
        columnB    *column
1085
        columnC    *column
1086
        columnD    *column
1087
        columnE    *column
1088
        columnF    *column
1089
}
1090

1091
func newQuery6[A any, B any, C any, D any, E any, F any](world *World, filter *filter, ids []ID, relations []RelationID,
1092
        cacheID cacheID, components []*componentStorage) Query6[A, B, C, D, E, F] {
13✔
1093
        var cache *cacheEntry
13✔
1094
        if cacheID != maxCacheID {
16✔
1095
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
1096
        }
3✔
1097

1098
        return Query6[A, B, C, D, E, F]{
13✔
1099
                world:      world,
13✔
1100
                filter:     filter,
13✔
1101
                relations:  relations,
13✔
1102
                cache:      cache,
13✔
1103
                lock:       world.lock(),
13✔
1104
                components: components,
13✔
1105
                cursor: cursor{
13✔
1106
                        archetype: -1,
13✔
1107
                        table:     -1,
13✔
1108
                        index:     0,
13✔
1109
                        maxIndex:  -1,
13✔
1110
                },
13✔
1111
        }
13✔
1112
}
1113

1114
// Next advances the query's cursor to the next entity.
1115
func (q *Query6[A, B, C, D, E, F]) Next() bool {
216✔
1116
        q.world.checkQueryNext(&q.cursor)
216✔
1117
        if int64(q.cursor.index) < q.cursor.maxIndex {
403✔
1118
                q.cursor.index++
187✔
1119
                return true
187✔
1120
        }
187✔
1121
        return q.nextTableOrArchetype()
29✔
1122
}
1123

1124
// Entity returns the current entity.
1125
func (q *Query6[A, B, C, D, E, F]) Entity() Entity {
204✔
1126
        q.world.checkQueryGet(&q.cursor)
204✔
1127
        return q.table.GetEntity(q.cursor.index)
204✔
1128
}
204✔
1129

1130
// Get returns the queried components of the current entity.
1131
//
1132
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
1133
func (q *Query6[A, B, C, D, E, F]) Get() (*A, *B, *C, *D, *E, *F) {
204✔
1134
        q.world.checkQueryGet(&q.cursor)
204✔
1135
        return (*A)(q.columnA.Get(q.cursor.index)),
204✔
1136
                (*B)(q.columnB.Get(q.cursor.index)),
204✔
1137
                (*C)(q.columnC.Get(q.cursor.index)),
204✔
1138
                (*D)(q.columnD.Get(q.cursor.index)),
204✔
1139
                (*E)(q.columnE.Get(q.cursor.index)),
204✔
1140
                (*F)(q.columnF.Get(q.cursor.index))
204✔
1141
}
204✔
1142

1143
// GetRelation returns the entity relation target of the component at the given index.
1144
func (q *Query6[A, B, C, D, E, F]) GetRelation(index int) Entity {
40✔
1145
        return q.components[index].columns[q.table.id].target
40✔
1146
}
40✔
1147

1148
// Count counts the entities matching this query.
1149
//
1150
// Has some overhead of iterating through archetypes.
1151
// However, this is still much faster than manual counting via iteration.
1152
//
1153
// Does not iterate or close the query.
1154
func (q *Query6[A, B, C, D, E, F]) Count() int {
11✔
1155
        if q.cache == nil {
19✔
1156
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
1157
        }
8✔
1158
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
1159
}
1160

1161
// Close closes the Query and unlocks the world.
1162
//
1163
// Automatically called when iteration completes.
1164
// Needs to be called only if breaking out of the query iteration or not iterating at all.
1165
func (q *Query6[A, B, C, D, E, F]) Close() {
13✔
1166
        q.cursor.archetype = -2
13✔
1167
        q.cursor.table = -2
13✔
1168
        q.tables = nil
13✔
1169
        q.table = nil
13✔
1170
        q.cache = nil
13✔
1171
        q.columnA = nil
13✔
1172
        q.columnB = nil
13✔
1173
        q.columnC = nil
13✔
1174
        q.columnD = nil
13✔
1175
        q.columnE = nil
13✔
1176
        q.columnF = nil
13✔
1177
        q.world.unlock(q.lock)
13✔
1178
}
13✔
1179

1180
func (q *Query6[A, B, C, D, E, F]) nextTableOrArchetype() bool {
29✔
1181
        if q.cache != nil {
36✔
1182
                return q.nextTable(q.cache.tables)
7✔
1183
        }
7✔
1184
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
23✔
1185
                return true
1✔
1186
        }
1✔
1187
        return q.nextArchetype()
21✔
1188
}
1189

1190
func (q *Query6[A, B, C, D, E, F]) nextArchetype() bool {
21✔
1191
        maxArchIndex := len(q.world.storage.archetypes) - 1
21✔
1192
        for q.cursor.archetype < maxArchIndex {
50✔
1193
                q.cursor.archetype++
29✔
1194
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
29✔
1195
                if !q.filter.matches(&archetype.mask) {
47✔
1196
                        continue
18✔
1197
                }
1198

1199
                if !archetype.HasRelations() {
17✔
1200
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
1201
                        if table.Len() > 0 {
14✔
1202
                                q.setTable(0, table)
7✔
1203
                                return true
7✔
1204
                        }
7✔
UNCOV
1205
                        continue
×
1206
                }
1207

1208
                q.tables = archetype.GetTables(q.relations)
3✔
1209
                q.cursor.table = -1
3✔
1210
                if q.nextTable(q.tables) {
6✔
1211
                        return true
3✔
1212
                }
3✔
1213
        }
1214
        q.Close()
10✔
1215
        return false
10✔
1216
}
1217

1218
func (q *Query6[A, B, C, D, E, F]) nextTable(tables []tableID) bool {
21✔
1219
        maxTableIndex := len(tables) - 1
21✔
1220
        for q.cursor.table < maxTableIndex {
33✔
1221
                q.cursor.table++
12✔
1222
                table := &q.world.storage.tables[tables[q.cursor.table]]
12✔
1223
                if table.Len() == 0 {
15✔
1224
                        continue
3✔
1225
                }
1226
                if !table.Matches(q.relations) {
10✔
1227
                        continue
1✔
1228
                }
1229
                q.setTable(q.cursor.table, table)
8✔
1230
                return true
8✔
1231
        }
1232
        if q.cache != nil {
16✔
1233
                q.Close()
3✔
1234
        }
3✔
1235
        return false
13✔
1236
}
1237

1238
func (q *Query6[A, B, C, D, E, F]) setTable(index int, table *table) {
15✔
1239
        q.cursor.table = index
15✔
1240
        q.table = table
15✔
1241
        q.columnA = q.components[0].columns[q.table.id]
15✔
1242
        q.columnB = q.components[1].columns[q.table.id]
15✔
1243
        q.columnC = q.components[2].columns[q.table.id]
15✔
1244
        q.columnD = q.components[3].columns[q.table.id]
15✔
1245
        q.columnE = q.components[4].columns[q.table.id]
15✔
1246
        q.columnF = q.components[5].columns[q.table.id]
15✔
1247
        q.cursor.index = 0
15✔
1248
        q.cursor.maxIndex = int64(q.table.Len() - 1)
15✔
1249
}
15✔
1250

1251
type queryResult7[A any, B any, C any, D any, E any, F any, G any] struct {
1252
        Entity Entity
1253
        A      *A
1254
        B      *B
1255
        C      *C
1256
        D      *D
1257
        E      *E
1258
        F      *F
1259
        G      *G
1260
}
1261

1262
// Query7 is a query for 7 components.
1263
// Use a [Filter7] to create one.
1264
//
1265
// Queries are one-time use iterators and must be re-created each time before iterating.
1266
//
1267
// See [Query2] for a usage example.
1268
type Query7[A any, B any, C any, D any, E any, F any, G any] struct {
1269
        world      *World
1270
        filter     *filter
1271
        relations  []RelationID
1272
        lock       uint8
1273
        cursor     cursor
1274
        tables     []tableID
1275
        table      *table
1276
        cache      *cacheEntry
1277
        components []*componentStorage
1278
        columnA    *column
1279
        columnB    *column
1280
        columnC    *column
1281
        columnD    *column
1282
        columnE    *column
1283
        columnF    *column
1284
        columnG    *column
1285
}
1286

1287
func newQuery7[A any, B any, C any, D any, E any, F any, G any](world *World, filter *filter, ids []ID, relations []RelationID,
1288
        cacheID cacheID, components []*componentStorage) Query7[A, B, C, D, E, F, G] {
13✔
1289
        var cache *cacheEntry
13✔
1290
        if cacheID != maxCacheID {
16✔
1291
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
1292
        }
3✔
1293

1294
        return Query7[A, B, C, D, E, F, G]{
13✔
1295
                world:      world,
13✔
1296
                filter:     filter,
13✔
1297
                relations:  relations,
13✔
1298
                cache:      cache,
13✔
1299
                lock:       world.lock(),
13✔
1300
                components: components,
13✔
1301
                cursor: cursor{
13✔
1302
                        archetype: -1,
13✔
1303
                        table:     -1,
13✔
1304
                        index:     0,
13✔
1305
                        maxIndex:  -1,
13✔
1306
                },
13✔
1307
        }
13✔
1308
}
1309

1310
// Next advances the query's cursor to the next entity.
1311
func (q *Query7[A, B, C, D, E, F, G]) Next() bool {
216✔
1312
        q.world.checkQueryNext(&q.cursor)
216✔
1313
        if int64(q.cursor.index) < q.cursor.maxIndex {
403✔
1314
                q.cursor.index++
187✔
1315
                return true
187✔
1316
        }
187✔
1317
        return q.nextTableOrArchetype()
29✔
1318
}
1319

1320
// Entity returns the current entity.
1321
func (q *Query7[A, B, C, D, E, F, G]) Entity() Entity {
204✔
1322
        q.world.checkQueryGet(&q.cursor)
204✔
1323
        return q.table.GetEntity(q.cursor.index)
204✔
1324
}
204✔
1325

1326
// Get returns the queried components of the current entity.
1327
//
1328
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
1329
func (q *Query7[A, B, C, D, E, F, G]) Get() (*A, *B, *C, *D, *E, *F, *G) {
204✔
1330
        q.world.checkQueryGet(&q.cursor)
204✔
1331
        return (*A)(q.columnA.Get(q.cursor.index)),
204✔
1332
                (*B)(q.columnB.Get(q.cursor.index)),
204✔
1333
                (*C)(q.columnC.Get(q.cursor.index)),
204✔
1334
                (*D)(q.columnD.Get(q.cursor.index)),
204✔
1335
                (*E)(q.columnE.Get(q.cursor.index)),
204✔
1336
                (*F)(q.columnF.Get(q.cursor.index)),
204✔
1337
                (*G)(q.columnG.Get(q.cursor.index))
204✔
1338
}
204✔
1339

1340
// GetRelation returns the entity relation target of the component at the given index.
1341
func (q *Query7[A, B, C, D, E, F, G]) GetRelation(index int) Entity {
40✔
1342
        return q.components[index].columns[q.table.id].target
40✔
1343
}
40✔
1344

1345
// Count counts the entities matching this query.
1346
//
1347
// Has some overhead of iterating through archetypes.
1348
// However, this is still much faster than manual counting via iteration.
1349
//
1350
// Does not iterate or close the query.
1351
func (q *Query7[A, B, C, D, E, F, G]) Count() int {
11✔
1352
        if q.cache == nil {
19✔
1353
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
1354
        }
8✔
1355
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
1356
}
1357

1358
// Close closes the Query and unlocks the world.
1359
//
1360
// Automatically called when iteration completes.
1361
// Needs to be called only if breaking out of the query iteration or not iterating at all.
1362
func (q *Query7[A, B, C, D, E, F, G]) Close() {
13✔
1363
        q.cursor.archetype = -2
13✔
1364
        q.cursor.table = -2
13✔
1365
        q.tables = nil
13✔
1366
        q.table = nil
13✔
1367
        q.cache = nil
13✔
1368
        q.columnA = nil
13✔
1369
        q.columnB = nil
13✔
1370
        q.columnC = nil
13✔
1371
        q.columnD = nil
13✔
1372
        q.columnE = nil
13✔
1373
        q.columnF = nil
13✔
1374
        q.columnG = nil
13✔
1375
        q.world.unlock(q.lock)
13✔
1376
}
13✔
1377

1378
func (q *Query7[A, B, C, D, E, F, G]) nextTableOrArchetype() bool {
29✔
1379
        if q.cache != nil {
36✔
1380
                return q.nextTable(q.cache.tables)
7✔
1381
        }
7✔
1382
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
23✔
1383
                return true
1✔
1384
        }
1✔
1385
        return q.nextArchetype()
21✔
1386
}
1387

1388
func (q *Query7[A, B, C, D, E, F, G]) nextArchetype() bool {
21✔
1389
        maxArchIndex := len(q.world.storage.archetypes) - 1
21✔
1390
        for q.cursor.archetype < maxArchIndex {
50✔
1391
                q.cursor.archetype++
29✔
1392
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
29✔
1393
                if !q.filter.matches(&archetype.mask) {
47✔
1394
                        continue
18✔
1395
                }
1396

1397
                if !archetype.HasRelations() {
17✔
1398
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
1399
                        if table.Len() > 0 {
14✔
1400
                                q.setTable(0, table)
7✔
1401
                                return true
7✔
1402
                        }
7✔
UNCOV
1403
                        continue
×
1404
                }
1405

1406
                q.tables = archetype.GetTables(q.relations)
3✔
1407
                q.cursor.table = -1
3✔
1408
                if q.nextTable(q.tables) {
6✔
1409
                        return true
3✔
1410
                }
3✔
1411
        }
1412
        q.Close()
10✔
1413
        return false
10✔
1414
}
1415

1416
func (q *Query7[A, B, C, D, E, F, G]) nextTable(tables []tableID) bool {
21✔
1417
        maxTableIndex := len(tables) - 1
21✔
1418
        for q.cursor.table < maxTableIndex {
33✔
1419
                q.cursor.table++
12✔
1420
                table := &q.world.storage.tables[tables[q.cursor.table]]
12✔
1421
                if table.Len() == 0 {
15✔
1422
                        continue
3✔
1423
                }
1424
                if !table.Matches(q.relations) {
10✔
1425
                        continue
1✔
1426
                }
1427
                q.setTable(q.cursor.table, table)
8✔
1428
                return true
8✔
1429
        }
1430
        if q.cache != nil {
16✔
1431
                q.Close()
3✔
1432
        }
3✔
1433
        return false
13✔
1434
}
1435

1436
func (q *Query7[A, B, C, D, E, F, G]) setTable(index int, table *table) {
15✔
1437
        q.cursor.table = index
15✔
1438
        q.table = table
15✔
1439
        q.columnA = q.components[0].columns[q.table.id]
15✔
1440
        q.columnB = q.components[1].columns[q.table.id]
15✔
1441
        q.columnC = q.components[2].columns[q.table.id]
15✔
1442
        q.columnD = q.components[3].columns[q.table.id]
15✔
1443
        q.columnE = q.components[4].columns[q.table.id]
15✔
1444
        q.columnF = q.components[5].columns[q.table.id]
15✔
1445
        q.columnG = q.components[6].columns[q.table.id]
15✔
1446
        q.cursor.index = 0
15✔
1447
        q.cursor.maxIndex = int64(q.table.Len() - 1)
15✔
1448
}
15✔
1449

1450
type queryResult8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
1451
        Entity Entity
1452
        A      *A
1453
        B      *B
1454
        C      *C
1455
        D      *D
1456
        E      *E
1457
        F      *F
1458
        G      *G
1459
        H      *H
1460
}
1461

1462
// Query8 is a query for 8 components.
1463
// Use a [Filter8] to create one.
1464
//
1465
// Queries are one-time use iterators and must be re-created each time before iterating.
1466
//
1467
// See [Query2] for a usage example.
1468
type Query8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
1469
        world      *World
1470
        filter     *filter
1471
        relations  []RelationID
1472
        lock       uint8
1473
        cursor     cursor
1474
        tables     []tableID
1475
        table      *table
1476
        cache      *cacheEntry
1477
        components []*componentStorage
1478
        columnA    *column
1479
        columnB    *column
1480
        columnC    *column
1481
        columnD    *column
1482
        columnE    *column
1483
        columnF    *column
1484
        columnG    *column
1485
        columnH    *column
1486
}
1487

1488
func newQuery8[A any, B any, C any, D any, E any, F any, G any, H any](world *World, filter *filter, ids []ID, relations []RelationID,
1489
        cacheID cacheID, components []*componentStorage) Query8[A, B, C, D, E, F, G, H] {
13✔
1490
        var cache *cacheEntry
13✔
1491
        if cacheID != maxCacheID {
16✔
1492
                cache = world.storage.getRegisteredFilter(cacheID)
3✔
1493
        }
3✔
1494

1495
        return Query8[A, B, C, D, E, F, G, H]{
13✔
1496
                world:      world,
13✔
1497
                filter:     filter,
13✔
1498
                relations:  relations,
13✔
1499
                cache:      cache,
13✔
1500
                lock:       world.lock(),
13✔
1501
                components: components,
13✔
1502
                cursor: cursor{
13✔
1503
                        archetype: -1,
13✔
1504
                        table:     -1,
13✔
1505
                        index:     0,
13✔
1506
                        maxIndex:  -1,
13✔
1507
                },
13✔
1508
        }
13✔
1509
}
1510

1511
// Next advances the query's cursor to the next entity.
1512
func (q *Query8[A, B, C, D, E, F, G, H]) Next() bool {
216✔
1513
        q.world.checkQueryNext(&q.cursor)
216✔
1514
        if int64(q.cursor.index) < q.cursor.maxIndex {
403✔
1515
                q.cursor.index++
187✔
1516
                return true
187✔
1517
        }
187✔
1518
        return q.nextTableOrArchetype()
29✔
1519
}
1520

1521
// Entity returns the current entity.
1522
func (q *Query8[A, B, C, D, E, F, G, H]) Entity() Entity {
204✔
1523
        q.world.checkQueryGet(&q.cursor)
204✔
1524
        return q.table.GetEntity(q.cursor.index)
204✔
1525
}
204✔
1526

1527
// Get returns the queried components of the current entity.
1528
//
1529
// ⚠️ Do not store the obtained pointers outside of the current context (i.e. the query loop)!
1530
func (q *Query8[A, B, C, D, E, F, G, H]) Get() (*A, *B, *C, *D, *E, *F, *G, *H) {
204✔
1531
        q.world.checkQueryGet(&q.cursor)
204✔
1532
        return (*A)(q.columnA.Get(q.cursor.index)),
204✔
1533
                (*B)(q.columnB.Get(q.cursor.index)),
204✔
1534
                (*C)(q.columnC.Get(q.cursor.index)),
204✔
1535
                (*D)(q.columnD.Get(q.cursor.index)),
204✔
1536
                (*E)(q.columnE.Get(q.cursor.index)),
204✔
1537
                (*F)(q.columnF.Get(q.cursor.index)),
204✔
1538
                (*G)(q.columnG.Get(q.cursor.index)),
204✔
1539
                (*H)(q.columnH.Get(q.cursor.index))
204✔
1540
}
204✔
1541

1542
// GetRelation returns the entity relation target of the component at the given index.
1543
func (q *Query8[A, B, C, D, E, F, G, H]) GetRelation(index int) Entity {
40✔
1544
        return q.components[index].columns[q.table.id].target
40✔
1545
}
40✔
1546

1547
// Count counts the entities matching this query.
1548
//
1549
// Has some overhead of iterating through archetypes.
1550
// However, this is still much faster than manual counting via iteration.
1551
//
1552
// Does not iterate or close the query.
1553
func (q *Query8[A, B, C, D, E, F, G, H]) Count() int {
11✔
1554
        if q.cache == nil {
19✔
1555
                return countQuery(&q.world.storage, q.filter, q.relations)
8✔
1556
        }
8✔
1557
        return countQueryCache(&q.world.storage, q.cache, q.relations)
3✔
1558
}
1559

1560
// Close closes the Query and unlocks the world.
1561
//
1562
// Automatically called when iteration completes.
1563
// Needs to be called only if breaking out of the query iteration or not iterating at all.
1564
func (q *Query8[A, B, C, D, E, F, G, H]) Close() {
13✔
1565
        q.cursor.archetype = -2
13✔
1566
        q.cursor.table = -2
13✔
1567
        q.tables = nil
13✔
1568
        q.table = nil
13✔
1569
        q.cache = nil
13✔
1570
        q.columnA = nil
13✔
1571
        q.columnB = nil
13✔
1572
        q.columnC = nil
13✔
1573
        q.columnD = nil
13✔
1574
        q.columnE = nil
13✔
1575
        q.columnF = nil
13✔
1576
        q.columnG = nil
13✔
1577
        q.columnH = nil
13✔
1578
        q.world.unlock(q.lock)
13✔
1579
}
13✔
1580

1581
func (q *Query8[A, B, C, D, E, F, G, H]) nextTableOrArchetype() bool {
29✔
1582
        if q.cache != nil {
36✔
1583
                return q.nextTable(q.cache.tables)
7✔
1584
        }
7✔
1585
        if q.cursor.archetype >= 0 && q.nextTable(q.tables) {
23✔
1586
                return true
1✔
1587
        }
1✔
1588
        return q.nextArchetype()
21✔
1589
}
1590

1591
func (q *Query8[A, B, C, D, E, F, G, H]) nextArchetype() bool {
21✔
1592
        maxArchIndex := len(q.world.storage.archetypes) - 1
21✔
1593
        for q.cursor.archetype < maxArchIndex {
50✔
1594
                q.cursor.archetype++
29✔
1595
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
29✔
1596
                if !q.filter.matches(&archetype.mask) {
47✔
1597
                        continue
18✔
1598
                }
1599

1600
                if !archetype.HasRelations() {
17✔
1601
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
1602
                        if table.Len() > 0 {
14✔
1603
                                q.setTable(0, table)
7✔
1604
                                return true
7✔
1605
                        }
7✔
UNCOV
1606
                        continue
×
1607
                }
1608

1609
                q.tables = archetype.GetTables(q.relations)
3✔
1610
                q.cursor.table = -1
3✔
1611
                if q.nextTable(q.tables) {
6✔
1612
                        return true
3✔
1613
                }
3✔
1614
        }
1615
        q.Close()
10✔
1616
        return false
10✔
1617
}
1618

1619
func (q *Query8[A, B, C, D, E, F, G, H]) nextTable(tables []tableID) bool {
21✔
1620
        maxTableIndex := len(tables) - 1
21✔
1621
        for q.cursor.table < maxTableIndex {
33✔
1622
                q.cursor.table++
12✔
1623
                table := &q.world.storage.tables[tables[q.cursor.table]]
12✔
1624
                if table.Len() == 0 {
15✔
1625
                        continue
3✔
1626
                }
1627
                if !table.Matches(q.relations) {
10✔
1628
                        continue
1✔
1629
                }
1630
                q.setTable(q.cursor.table, table)
8✔
1631
                return true
8✔
1632
        }
1633
        if q.cache != nil {
16✔
1634
                q.Close()
3✔
1635
        }
3✔
1636
        return false
13✔
1637
}
1638

1639
func (q *Query8[A, B, C, D, E, F, G, H]) setTable(index int, table *table) {
15✔
1640
        q.cursor.table = index
15✔
1641
        q.table = table
15✔
1642
        q.columnA = q.components[0].columns[q.table.id]
15✔
1643
        q.columnB = q.components[1].columns[q.table.id]
15✔
1644
        q.columnC = q.components[2].columns[q.table.id]
15✔
1645
        q.columnD = q.components[3].columns[q.table.id]
15✔
1646
        q.columnE = q.components[4].columns[q.table.id]
15✔
1647
        q.columnF = q.components[5].columns[q.table.id]
15✔
1648
        q.columnG = q.components[6].columns[q.table.id]
15✔
1649
        q.columnH = q.components[7].columns[q.table.id]
15✔
1650
        q.cursor.index = 0
15✔
1651
        q.cursor.maxIndex = int64(q.table.Len() - 1)
15✔
1652
}
15✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc