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

mlange-42 / ark / 13768706324

10 Mar 2025 03:38PM CUT coverage: 99.437%. Remained the same
13768706324

Pull #179

github

web-flow
Merge 8094be2da into 093db9782
Pull Request #179: Fix false-positive debug checks

6538 of 6575 relevant lines covered (99.44%)

27299.41 hits per line

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

98.91
/ecs/query.go
1
package ecs
2

3
import "unsafe"
4

5
// Query is an unsafe query.
6
// It is significantly slower than type-safe generic queries like [Query2],
7
// and should only be used when component types are not known at compile time.
8
type Query struct {
9
        world     *World
10
        filter    filter
11
        relations []RelationID
12
        lock      uint8
13
        cursor    cursor
14
        tables    []tableID
15
        table     *table
16
}
17

18
func newQuery(world *World, filter filter, relations []RelationID) Query {
14✔
19
        return Query{
14✔
20
                world:     world,
14✔
21
                filter:    filter,
14✔
22
                relations: relations,
14✔
23
                lock:      world.lock(),
14✔
24
                cursor: cursor{
14✔
25
                        archetype: -1,
14✔
26
                        table:     -1,
14✔
27
                        index:     0,
14✔
28
                        maxIndex:  -1,
14✔
29
                },
14✔
30
        }
14✔
31
}
14✔
32

33
// Next advances the query's cursor to the next entity.
34
func (q *Query) Next() bool {
285✔
35
        q.world.checkQueryNext(&q.cursor)
285✔
36
        if int64(q.cursor.index) < q.cursor.maxIndex {
549✔
37
                q.cursor.index++
264✔
38
                return true
264✔
39
        }
264✔
40
        return q.nextTableOrArchetype()
21✔
41
}
42

43
// Entity returns the current entity.
44
func (q *Query) Entity() Entity {
76✔
45
        q.world.checkQueryGet(&q.cursor)
76✔
46
        return q.table.GetEntity(q.cursor.index)
76✔
47
}
76✔
48

49
// Get returns the queried components of the current entity.
50
//
51
// ⚠️ Do not store the obtained pointer outside of the current context (i.e. the query loop)!
52
func (q *Query) Get(comp ID) unsafe.Pointer {
72✔
53
        q.world.checkQueryGet(&q.cursor)
72✔
54
        return q.table.Get(comp, uintptr(q.cursor.index))
72✔
55
}
72✔
56

57
// Has returns whether the current entity has the given component.
58
func (q *Query) Has(comp ID) bool {
40✔
59
        return q.table.Has(comp)
40✔
60
}
40✔
61

62
// GetRelation returns the entity relation target of the component at the given index.
63
func (q *Query) GetRelation(comp ID) Entity {
10✔
64
        return q.table.GetRelation(comp)
10✔
65
}
10✔
66

67
// Count returns the number of entities matching this query.
68
func (q *Query) Count() int {
10✔
69
        return countQuery(&q.world.storage, &q.filter, q.relations)
10✔
70
}
10✔
71

72
// IDs returns the IDs of all component of the current [Entity]n.
73
func (q *Query) IDs() IDs {
10✔
74
        return newIDs(q.table.ids)
10✔
75
}
10✔
76

77
// Close closes the Query and unlocks the world.
78
//
79
// Automatically called when iteration completes.
80
// Needs to be called only if breaking out of the query iteration or not iterating at all.
81
func (q *Query) Close() {
14✔
82
        q.cursor.archetype = -2
14✔
83
        q.cursor.table = -2
14✔
84
        q.tables = nil
14✔
85
        q.table = nil
14✔
86
        q.world.unlock(q.lock)
14✔
87
}
14✔
88

89
func (q *Query) nextTableOrArchetype() bool {
21✔
90
        if q.cursor.archetype >= 0 && q.nextTable() {
22✔
91
                return true
1✔
92
        }
1✔
93
        return q.nextArchetype()
20✔
94
}
95

96
func (q *Query) nextArchetype() bool {
20✔
97
        maxArchIndex := len(q.world.storage.archetypes) - 1
20✔
98
        for q.cursor.archetype < maxArchIndex {
43✔
99
                q.cursor.archetype++
23✔
100
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
23✔
101
                if !q.filter.matches(&archetype.mask) {
35✔
102
                        continue
12✔
103
                }
104

105
                if !archetype.HasRelations() {
18✔
106
                        table := &q.world.storage.tables[archetype.tables[0]]
8✔
107
                        if table.Len() > 0 {
15✔
108
                                q.setTable(0, table)
7✔
109
                                return true
7✔
110
                        }
7✔
111
                        continue
1✔
112
                }
113

114
                q.tables = archetype.GetTables(q.relations)
2✔
115
                q.cursor.table = -1
2✔
116
                if q.nextTable() {
4✔
117
                        return true
2✔
118
                }
2✔
119
        }
120
        q.Close()
10✔
121
        return false
10✔
122
}
123

124
func (q *Query) nextTable() bool {
12✔
125
        maxTableIndex := len(q.tables) - 1
12✔
126
        for q.cursor.table < maxTableIndex {
16✔
127
                q.cursor.table++
4✔
128
                table := &q.world.storage.tables[q.tables[q.cursor.table]]
4✔
129
                if table.Len() == 0 {
5✔
130
                        continue
1✔
131
                }
132
                if !table.Matches(q.relations) {
3✔
133
                        continue
×
134
                }
135
                q.setTable(q.cursor.table, table)
3✔
136
                return true
3✔
137
        }
138
        return false
9✔
139
}
140

141
func (q *Query) setTable(index int, table *table) {
10✔
142
        q.cursor.table = index
10✔
143
        q.table = table
10✔
144
        q.cursor.index = 0
10✔
145
        q.cursor.maxIndex = int64(q.table.Len() - 1)
10✔
146
}
10✔
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