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

mlange-42 / ark / 13642177570

03 Mar 2025 11:00PM CUT coverage: 99.261% (+0.4%) from 98.883%
13642177570

Pull #119

github

web-flow
Merge 0cb98822c into 3f4452870
Pull Request #119: Add more unit tests

3 of 3 new or added lines in 1 file covered. (100.0%)

5772 of 5815 relevant lines covered (99.26%)

34304.36 hits per line

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

98.11
/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 {
13✔
19
        return Query{
13✔
20
                world:     world,
13✔
21
                filter:    filter,
13✔
22
                relations: relations,
13✔
23
                lock:      world.lock(),
13✔
24
                cursor: cursor{
13✔
25
                        archetype: -1,
13✔
26
                        table:     -1,
13✔
27
                        index:     0,
13✔
28
                        maxIndex:  -1,
13✔
29
                },
13✔
30
        }
13✔
31
}
13✔
32

33
// Next advances the query's cursor to the next entity.
34
func (q *Query) Next() bool {
84✔
35
        q.world.checkQueryNext(&q.cursor)
84✔
36
        if int64(q.cursor.index) < q.cursor.maxIndex {
149✔
37
                q.cursor.index++
65✔
38
                return true
65✔
39
        }
65✔
40
        return q.nextTableOrArchetype()
19✔
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
func (q *Query) Get(comp ID) unsafe.Pointer {
72✔
51
        q.world.checkQueryGet(&q.cursor)
72✔
52
        return q.table.Get(comp, uintptr(q.cursor.index))
72✔
53
}
72✔
54

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

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

65
// Count returns the number of entities matching this query.
66
func (q *Query) Count() int {
10✔
67
        count := 0
10✔
68
        for i := range q.world.storage.archetypes {
39✔
69
                archetype := &q.world.storage.archetypes[i]
29✔
70
                if !q.filter.matches(&archetype.mask) {
41✔
71
                        continue
12✔
72
                }
73

74
                if !archetype.HasRelations() {
30✔
75
                        table := &q.world.storage.tables[archetype.tables[0]]
13✔
76
                        count += table.Len()
13✔
77
                        continue
13✔
78
                }
79

80
                tables := archetype.GetTables(q.relations)
4✔
81
                for _, tab := range tables {
8✔
82
                        table := &q.world.storage.tables[tab]
4✔
83
                        if !table.Matches(q.relations) {
4✔
84
                                continue
×
85
                        }
86
                        count += table.Len()
4✔
87
                }
88
        }
89
        return count
10✔
90
}
91

92
// IDs returns the component IDs for the archetype of the [Entity] at the iterator's current position.
93
//
94
// Returns a copy of the archetype's component IDs slice, for safety.
95
// This means that the result can be manipulated safely,
96
// but also that calling the method may incur some significant cost.
97
func (q *Query) IDs() []ID {
10✔
98
        return append([]ID{}, q.table.ids...)
10✔
99
}
10✔
100

101
// Close closes the Query and unlocks the world.
102
//
103
// Automatically called when iteration finishes.
104
// Needs to be called only if breaking out of the query iteration or not iterating at all.
105
func (q *Query) Close() {
13✔
106
        q.cursor.archetype = -2
13✔
107
        q.cursor.table = -2
13✔
108
        q.tables = nil
13✔
109
        q.table = nil
13✔
110
        q.world.unlock(q.lock)
13✔
111
}
13✔
112

113
func (q *Query) nextTableOrArchetype() bool {
19✔
114
        if q.cursor.archetype >= 0 && q.nextTable() {
20✔
115
                return true
1✔
116
        }
1✔
117
        return q.nextArchetype()
18✔
118
}
119

120
func (q *Query) nextArchetype() bool {
18✔
121
        maxArchIndex := len(q.world.storage.archetypes) - 1
18✔
122
        for q.cursor.archetype < maxArchIndex {
40✔
123
                q.cursor.archetype++
22✔
124
                archetype := &q.world.storage.archetypes[q.cursor.archetype]
22✔
125
                if !q.filter.matches(&archetype.mask) {
34✔
126
                        continue
12✔
127
                }
128

129
                if !archetype.HasRelations() {
16✔
130
                        table := &q.world.storage.tables[archetype.tables[0]]
7✔
131
                        if table.Len() > 0 {
13✔
132
                                q.setTable(0, table)
6✔
133
                                return true
6✔
134
                        }
6✔
135
                        continue
1✔
136
                }
137

138
                q.tables = archetype.GetTables(q.relations)
2✔
139
                q.cursor.table = -1
2✔
140
                if q.nextTable() {
4✔
141
                        return true
2✔
142
                }
2✔
143
        }
144
        q.Close()
9✔
145
        return false
9✔
146
}
147

148
func (q *Query) nextTable() bool {
11✔
149
        maxTableIndex := len(q.tables) - 1
11✔
150
        for q.cursor.table < maxTableIndex {
15✔
151
                q.cursor.table++
4✔
152
                table := &q.world.storage.tables[q.tables[q.cursor.table]]
4✔
153
                if table.Len() == 0 {
5✔
154
                        continue
1✔
155
                }
156
                if !table.Matches(q.relations) {
3✔
157
                        continue
×
158
                }
159
                q.setTable(q.cursor.table, table)
3✔
160
                return true
3✔
161
        }
162
        return false
8✔
163
}
164

165
func (q *Query) setTable(index int, table *table) {
9✔
166
        q.cursor.table = index
9✔
167
        q.table = table
9✔
168
        q.cursor.index = 0
9✔
169
        q.cursor.maxIndex = int64(q.table.entities.Len() - 1)
9✔
170
}
9✔
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