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

mlange-42 / ark / 13742253133

08 Mar 2025 10:59PM CUT coverage: 99.43%. Remained the same
13742253133

push

github

web-flow
Update CHANGELOG for v0.3.0 release (#166)

6456 of 6493 relevant lines covered (99.43%)

28632.4 hits per line

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

99.48
/ecs/archetype.go
1
package ecs
2

3
import (
4
        "math"
5
        "reflect"
6
        "slices"
7

8
        "github.com/mlange-42/ark/ecs/stats"
9
)
10

11
type archetypeID uint32
12

13
// maxArchetypeID is used as unassigned archetype ID.
14
const maxArchetypeID = math.MaxUint32
15

16
type archetype struct {
17
        id             archetypeID
18
        node           nodeID
19
        mask           bitMask
20
        components     []ID                     // components IDs of the archetype in arbitrary order
21
        componentsMap  []int16                  // mapping from component IDs to column indices; -1 indicates none
22
        isRelation     []bool                   // whether columns are relations components, indexed by column index
23
        relationTables []map[entityID]*tableIDs // lookup for relation targets of tables, indexed by column index
24
        tables         []tableID                // all active tables
25
        freeTables     []tableID                // all inactive/free tables
26
        numRelations   uint8                    // number of relation components
27
}
28

29
type tableIDs struct {
30
        tables []tableID
31
}
32

33
func newArchetype(id archetypeID, node nodeID, mask *bitMask, components []ID, tables []tableID, reg *componentRegistry) archetype {
622✔
34
        componentsMap := make([]int16, maskTotalBits)
622✔
35
        for i := range maskTotalBits {
159,854✔
36
                componentsMap[i] = -1
159,232✔
37
        }
159,232✔
38
        for i, id := range components {
2,085✔
39
                componentsMap[id.id] = int16(i)
1,463✔
40
        }
1,463✔
41

42
        numRelations := uint8(0)
622✔
43
        isRelation := make([]bool, len(components))
622✔
44
        relationTables := make([]map[entityID]*tableIDs, len(components))
622✔
45
        for i, id := range components {
2,085✔
46
                if reg.IsRelation[id.id] {
1,518✔
47
                        isRelation[i] = true
55✔
48
                        relationTables[i] = map[entityID]*tableIDs{}
55✔
49
                        numRelations++
55✔
50
                }
55✔
51
        }
52
        return archetype{
622✔
53
                id:             id,
622✔
54
                node:           node,
622✔
55
                mask:           *mask,
622✔
56
                components:     components,
622✔
57
                componentsMap:  componentsMap,
622✔
58
                isRelation:     isRelation,
622✔
59
                tables:         tables,
622✔
60
                numRelations:   numRelations,
622✔
61
                relationTables: relationTables,
622✔
62
        }
622✔
63
}
64

65
func (a *archetype) HasRelations() bool {
509,962✔
66
        return a.numRelations > 0
509,962✔
67
}
509,962✔
68

69
func (a *archetype) GetTable(storage *storage, relations []RelationID) (*table, bool) {
507,349✔
70
        if len(a.tables) == 0 {
507,760✔
71
                return nil, false
411✔
72
        }
411✔
73
        if !a.HasRelations() {
1,013,210✔
74
                return &storage.tables[a.tables[0]], true
506,272✔
75
        }
506,272✔
76
        index := a.componentsMap[relations[0].component.id]
666✔
77
        tables, ok := a.relationTables[index][relations[0].target.id]
666✔
78
        if !ok {
744✔
79
                return nil, false
78✔
80
        }
78✔
81
        for _, t := range tables.tables {
1,195✔
82
                table := &storage.tables[t]
607✔
83
                if table.MatchesExact(relations) {
1,193✔
84
                        return table, true
586✔
85
                }
586✔
86
        }
87
        return nil, false
2✔
88
}
89

90
func (a *archetype) GetTables(relations []RelationID) []tableID {
92✔
91
        if !a.HasRelations() || len(relations) == 0 {
127✔
92
                return a.tables
35✔
93
        }
35✔
94
        index := a.componentsMap[relations[0].component.id]
57✔
95
        if tables, ok := a.relationTables[index][relations[0].target.id]; ok {
114✔
96
                return tables.tables
57✔
97
        }
57✔
98
        return nil
×
99
}
100

101
func (a *archetype) GetFreeTable() (tableID, bool) {
491✔
102
        if len(a.freeTables) == 0 {
980✔
103
                return 0, false
489✔
104
        }
489✔
105
        last := len(a.freeTables) - 1
2✔
106
        table := a.freeTables[last]
2✔
107

2✔
108
        a.freeTables = a.freeTables[:last]
2✔
109

2✔
110
        return table, true
2✔
111
}
112

113
func (a *archetype) FreeTable(table tableID) {
13✔
114
        // TODO: can we speed this up for large numbers of relation targets?
13✔
115
        index := slices.Index(a.tables, table)
13✔
116
        last := len(a.tables) - 1
13✔
117

13✔
118
        if index != last {
18✔
119
                a.tables[index], a.tables[last] = a.tables[last], a.tables[index]
5✔
120
        }
5✔
121
        a.tables = a.tables[:last]
13✔
122

13✔
123
        a.freeTables = append(a.freeTables, table)
13✔
124
}
125

126
func (a *archetype) AddTable(table *table) {
491✔
127
        a.tables = append(a.tables, table.id)
491✔
128
        if !a.HasRelations() {
852✔
129
                return
361✔
130
        }
361✔
131

132
        for i := range table.ids {
611✔
133
                column := &table.columns[i]
481✔
134
                if !column.isRelation {
821✔
135
                        continue
340✔
136
                }
137
                target := column.target
141✔
138
                relations := a.relationTables[i]
141✔
139

141✔
140
                if tables, ok := relations[target.id]; ok {
147✔
141
                        tables.tables = append(tables.tables, table.id)
6✔
142
                } else {
141✔
143
                        relations[target.id] = &tableIDs{tables: []tableID{table.id}}
135✔
144
                }
135✔
145
        }
146
}
147

148
func (a *archetype) RemoveTarget(entity Entity) {
16✔
149
        for i := range a.relationTables {
43✔
150
                if !a.isRelation[i] {
35✔
151
                        continue
8✔
152
                }
153
                delete(a.relationTables[i], entity.id)
19✔
154
        }
155
}
156

157
func (a *archetype) Reset(storage *storage) {
4✔
158
        if !a.HasRelations() {
7✔
159
                storage.tables[a.tables[0]].Reset()
3✔
160
                return
3✔
161
        }
3✔
162

163
        for _, tab := range a.tables {
2✔
164
                table := &storage.tables[tab]
1✔
165
                table.Reset()
1✔
166
        }
1✔
167

168
        for i := len(a.tables) - 1; i >= 0; i-- {
2✔
169
                storage.cache.removeTable(storage, &storage.tables[a.tables[i]])
1✔
170
                a.FreeTable(a.tables[i])
1✔
171
        }
1✔
172

173
        for _, m := range a.relationTables {
3✔
174
                for key := range m {
3✔
175
                        delete(m, key)
1✔
176
                }
1✔
177
        }
178
}
179

180
// Stats generates statistics for an archetype.
181
func (a *archetype) Stats(storage *storage) stats.Archetype {
4✔
182
        ids := a.components
4✔
183
        aCompCount := len(ids)
4✔
184
        aTypes := make([]reflect.Type, aCompCount)
4✔
185
        for j, id := range ids {
12✔
186
                aTypes[j], _ = storage.registry.ComponentType(id.id)
8✔
187
        }
8✔
188

189
        cap := 0
4✔
190
        count := 0
4✔
191
        memory := 0
4✔
192
        var tableStats []stats.Table
4✔
193
        if a.tables != nil {
8✔
194
                tableStats = make([]stats.Table, len(a.tables))
4✔
195
                for i, id := range a.tables {
9✔
196
                        table := &storage.tables[id]
5✔
197
                        tableStats[i] = table.Stats(&storage.registry)
5✔
198
                        stats := &tableStats[i]
5✔
199
                        cap += stats.Capacity
5✔
200
                        count += stats.Size
5✔
201
                        memory += stats.Memory
5✔
202
                }
5✔
203
        }
204

205
        memPerEntity := 0
4✔
206
        intIDs := make([]uint8, len(ids))
4✔
207
        for j, id := range ids {
12✔
208
                intIDs[j] = id.id
8✔
209
                memPerEntity += int(aTypes[j].Size())
8✔
210
        }
8✔
211

212
        return stats.Archetype{
4✔
213
                FreeTables:      len(a.freeTables),
4✔
214
                NumRelations:    int(a.numRelations),
4✔
215
                Components:      aCompCount,
4✔
216
                ComponentIDs:    intIDs,
4✔
217
                ComponentTypes:  aTypes,
4✔
218
                Memory:          memory,
4✔
219
                MemoryPerEntity: memPerEntity,
4✔
220
                Size:            count,
4✔
221
                Capacity:        cap,
4✔
222
                Tables:          tableStats,
4✔
223
        }
4✔
224
}
225

226
// UpdateStats updates statistics for an archetype.
227
func (a *archetype) UpdateStats(stats *stats.Archetype, storage *storage) {
7✔
228
        arches := a.tables
7✔
229

7✔
230
        cap := 0
7✔
231
        count := 0
7✔
232
        memory := 0
7✔
233

7✔
234
        cntOld := int32(len(stats.Tables))
7✔
235
        cntNew := int32(len(arches))
7✔
236
        if cntNew < cntOld {
8✔
237
                stats.Tables = stats.Tables[:cntNew]
1✔
238
                cntOld = cntNew
1✔
239
        }
1✔
240
        var i int32
7✔
241
        for i = 0; i < cntOld; i++ {
14✔
242
                tableStats := &stats.Tables[i]
7✔
243
                table := &storage.tables[arches[i]]
7✔
244
                table.UpdateStats(stats, tableStats, &storage.registry)
7✔
245
                cap += tableStats.Capacity
7✔
246
                count += tableStats.Size
7✔
247
                memory += tableStats.Memory
7✔
248
        }
7✔
249
        for i = cntOld; i < cntNew; i++ {
8✔
250
                table := &storage.tables[arches[i]]
1✔
251
                tableStats := table.Stats(&storage.registry)
1✔
252
                stats.Tables = append(stats.Tables, tableStats)
1✔
253
                cap += tableStats.Capacity
1✔
254
                count += tableStats.Size
1✔
255
                memory += tableStats.Memory
1✔
256
        }
1✔
257

258
        stats.FreeTables = len(a.freeTables)
7✔
259
        stats.Capacity = cap
7✔
260
        stats.Size = count
7✔
261
        stats.Memory = memory
7✔
262
}
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