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

mlange-42 / ark / 14191219880

01 Apr 2025 08:44AM CUT coverage: 99.832%. Remained the same
14191219880

push

github

web-flow
Add awesome-go badge (#229)

8298 of 8312 relevant lines covered (99.83%)

20284.26 hits per line

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

99.55
/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
        components     []ID                     // components IDs of the archetype in arbitrary order
18
        itemSizes      []uint32                 // item size per component index
19
        componentsMap  []int16                  // mapping from component IDs to column indices; -1 indicates none
20
        isRelation     []bool                   // whether columns are relations components, indexed by column index
21
        relationTables []map[entityID]*tableIDs // lookup for relation targets of tables, indexed by column index
22
        tables         []tableID                // all active tables
23
        freeTables     []tableID                // all inactive/free tables
24
        zeroValue      []byte                   // zero value with the size of the largest item type, for fast zeroing
25
        mask           *bitMask
26
        id             archetypeID
27
        node           nodeID
28
        numRelations   uint8 // number of relation components
29
}
30

31
type tableIDs struct {
32
        tables []tableID
33
}
34

35
func newArchetype(id archetypeID, node nodeID, mask *bitMask, components []ID, tables []tableID, reg *componentRegistry) archetype {
734✔
36
        componentsMap := make([]int16, maskTotalBits)
734✔
37
        for i := range maskTotalBits {
188,638✔
38
                componentsMap[i] = -1
187,904✔
39
        }
187,904✔
40

41
        sizes := make([]uint32, len(components))
734✔
42
        var maxSize uintptr = entitySize
734✔
43
        for i, id := range components {
2,675✔
44
                componentsMap[id.id] = int16(i)
1,941✔
45
                tp := reg.Types[id.id]
1,941✔
46

1,941✔
47
                itemSize := sizeOf(tp)
1,941✔
48
                sizes[i] = uint32(itemSize)
1,941✔
49
                if itemSize > maxSize {
2,410✔
50
                        maxSize = itemSize
469✔
51
                }
469✔
52
        }
53
        var zeroValue []byte
734✔
54
        if maxSize > 0 {
1,468✔
55
                zeroValue = make([]byte, maxSize)
734✔
56
        }
734✔
57

58
        numRelations := uint8(0)
734✔
59
        isRelation := make([]bool, len(components))
734✔
60
        relationTables := make([]map[entityID]*tableIDs, len(components))
734✔
61
        for i, id := range components {
2,675✔
62
                if reg.IsRelation[id.id] {
2,004✔
63
                        isRelation[i] = true
63✔
64
                        relationTables[i] = map[entityID]*tableIDs{}
63✔
65
                        numRelations++
63✔
66
                }
63✔
67
        }
68
        return archetype{
734✔
69
                id:             id,
734✔
70
                node:           node,
734✔
71
                mask:           mask,
734✔
72
                components:     components,
734✔
73
                itemSizes:      sizes,
734✔
74
                componentsMap:  componentsMap,
734✔
75
                isRelation:     isRelation,
734✔
76
                tables:         tables,
734✔
77
                numRelations:   numRelations,
734✔
78
                relationTables: relationTables,
734✔
79
                zeroValue:      zeroValue,
734✔
80
        }
734✔
81
}
82

83
func (a *archetype) HasRelations() bool {
498,778✔
84
        return a.numRelations > 0
498,778✔
85
}
498,778✔
86

87
func (a *archetype) GetTable(storage *storage, relations []RelationID) (*table, bool) {
495,779✔
88
        if len(a.tables) == 0 {
496,259✔
89
                return nil, false
480✔
90
        }
480✔
91
        if !a.HasRelations() {
989,906✔
92
                return &storage.tables[a.tables[0]], true
494,607✔
93
        }
494,607✔
94
        if uint8(len(relations)) < a.numRelations {
693✔
95
                panic("relation targets must be fully specified")
1✔
96
        }
97
        index := a.componentsMap[relations[0].component.id]
691✔
98
        tables, ok := a.relationTables[index][relations[0].target.id]
691✔
99
        if !ok {
782✔
100
                return nil, false
91✔
101
        }
91✔
102
        for _, t := range tables.tables {
1,238✔
103
                table := &storage.tables[t]
638✔
104
                if table.MatchesExact(relations) {
1,235✔
105
                        return table, true
597✔
106
                }
597✔
107
        }
108
        return nil, false
3✔
109
}
110

111
func (a *archetype) GetTables(relations []RelationID) []tableID {
133✔
112
        if !a.HasRelations() || len(relations) == 0 {
179✔
113
                return a.tables
46✔
114
        }
46✔
115
        index := a.componentsMap[relations[0].component.id]
87✔
116
        if tables, ok := a.relationTables[index][relations[0].target.id]; ok {
174✔
117
                return tables.tables
87✔
118
        }
87✔
119
        return nil
×
120
}
121

122
func (a *archetype) GetFreeTable() (tableID, bool) {
574✔
123
        if len(a.freeTables) == 0 {
1,144✔
124
                return 0, false
570✔
125
        }
570✔
126
        last := len(a.freeTables) - 1
4✔
127
        table := a.freeTables[last]
4✔
128

4✔
129
        a.freeTables = a.freeTables[:last]
4✔
130

4✔
131
        return table, true
4✔
132
}
133

134
func (a *archetype) FreeTable(table tableID) {
15✔
135
        // TODO: can we speed this up for large numbers of relation targets?
15✔
136
        index := slices.Index(a.tables, table)
15✔
137
        last := len(a.tables) - 1
15✔
138

15✔
139
        if index != last {
21✔
140
                a.tables[index], a.tables[last] = a.tables[last], a.tables[index]
6✔
141
        }
6✔
142
        a.tables = a.tables[:last]
15✔
143

15✔
144
        a.freeTables = append(a.freeTables, table)
15✔
145
}
146

147
func (a *archetype) AddTable(table *table) {
574✔
148
        a.tables = append(a.tables, table.id)
574✔
149
        if !a.HasRelations() {
995✔
150
                return
421✔
151
        }
421✔
152

153
        for i := range table.ids {
853✔
154
                column := &table.columns[i]
700✔
155
                if !column.isRelation {
1,233✔
156
                        continue
533✔
157
                }
158
                target := column.target
167✔
159
                relations := a.relationTables[i]
167✔
160

167✔
161
                if tables, ok := relations[target.id]; ok {
175✔
162
                        tables.tables = append(tables.tables, table.id)
8✔
163
                } else {
167✔
164
                        relations[target.id] = &tableIDs{tables: []tableID{table.id}}
159✔
165
                }
159✔
166
        }
167
}
168

169
func (a *archetype) RemoveTarget(entity Entity) {
19✔
170
        for i := range a.relationTables {
55✔
171
                if !a.isRelation[i] {
47✔
172
                        continue
11✔
173
                }
174
                delete(a.relationTables[i], entity.id)
25✔
175
        }
176
}
177

178
func (a *archetype) Reset(storage *storage) {
4✔
179
        if !a.HasRelations() {
7✔
180
                storage.tables[a.tables[0]].Reset()
3✔
181
                return
3✔
182
        }
3✔
183

184
        for _, tab := range a.tables {
2✔
185
                table := &storage.tables[tab]
1✔
186
                table.Reset()
1✔
187
        }
1✔
188

189
        for i := len(a.tables) - 1; i >= 0; i-- {
2✔
190
                storage.cache.removeTable(storage, &storage.tables[a.tables[i]])
1✔
191
                a.FreeTable(a.tables[i])
1✔
192
        }
1✔
193

194
        for _, m := range a.relationTables {
3✔
195
                for key := range m {
3✔
196
                        delete(m, key)
1✔
197
                }
1✔
198
        }
199
}
200

201
// Stats generates statistics for an archetype.
202
func (a *archetype) Stats(storage *storage) stats.Archetype {
4✔
203
        ids := a.components
4✔
204
        aTypes := make([]reflect.Type, len(ids))
4✔
205
        for j, id := range ids {
12✔
206
                aTypes[j], _ = storage.registry.ComponentType(id.id)
8✔
207
        }
8✔
208

209
        memPerEntity := int(entitySize)
4✔
210
        intIDs := make([]uint8, len(ids))
4✔
211
        for j, id := range ids {
12✔
212
                intIDs[j] = id.id
8✔
213
                memPerEntity += int(a.itemSizes[j])
8✔
214
        }
8✔
215

216
        cap := 0
4✔
217
        count := 0
4✔
218
        memory := 0
4✔
219
        memoryUsed := 0
4✔
220
        tableStats := make([]stats.Table, len(a.tables))
4✔
221
        for i, id := range a.tables {
7✔
222
                table := &storage.tables[id]
3✔
223
                tableStats[i] = table.Stats(memPerEntity, &storage.registry)
3✔
224
                stats := &tableStats[i]
3✔
225
                cap += stats.Capacity
3✔
226
                count += stats.Size
3✔
227
                memory += stats.Memory
3✔
228
                memoryUsed += stats.MemoryUsed
3✔
229
        }
3✔
230
        for _, id := range a.freeTables {
6✔
231
                table := &storage.tables[id]
2✔
232
                cap += int(table.cap)
2✔
233
                memory += memPerEntity * int(table.cap)
2✔
234
        }
2✔
235

236
        return stats.Archetype{
4✔
237
                FreeTables:      len(a.freeTables),
4✔
238
                NumRelations:    int(a.numRelations),
4✔
239
                ComponentIDs:    intIDs,
4✔
240
                ComponentTypes:  aTypes,
4✔
241
                Memory:          memory,
4✔
242
                MemoryUsed:      memoryUsed,
4✔
243
                MemoryPerEntity: memPerEntity,
4✔
244
                Size:            count,
4✔
245
                Capacity:        cap,
4✔
246
                Tables:          tableStats,
4✔
247
        }
4✔
248
}
249

250
// UpdateStats updates statistics for an archetype.
251
func (a *archetype) UpdateStats(stats *stats.Archetype, storage *storage) {
11✔
252
        tables := a.tables
11✔
253

11✔
254
        cap := 0
11✔
255
        count := 0
11✔
256
        memory := 0
11✔
257
        memoryUsed := 0
11✔
258

11✔
259
        cntOld := int32(len(stats.Tables))
11✔
260
        cntNew := int32(len(tables))
11✔
261
        if cntNew < cntOld {
12✔
262
                stats.Tables = stats.Tables[:cntNew]
1✔
263
                cntOld = cntNew
1✔
264
        }
1✔
265
        var i int32
11✔
266
        for i = 0; i < cntOld; i++ {
22✔
267
                tableStats := &stats.Tables[i]
11✔
268
                table := &storage.tables[tables[i]]
11✔
269
                table.UpdateStats(stats.MemoryPerEntity, tableStats, &storage.registry)
11✔
270
                cap += tableStats.Capacity
11✔
271
                count += tableStats.Size
11✔
272
                memory += tableStats.Memory
11✔
273
                memoryUsed += tableStats.MemoryUsed
11✔
274
        }
11✔
275
        for i = cntOld; i < cntNew; i++ {
14✔
276
                table := &storage.tables[tables[i]]
3✔
277
                tableStats := table.Stats(stats.MemoryPerEntity, &storage.registry)
3✔
278
                stats.Tables = append(stats.Tables, tableStats)
3✔
279
                cap += tableStats.Capacity
3✔
280
                count += tableStats.Size
3✔
281
                memory += tableStats.Memory
3✔
282
                memoryUsed += tableStats.MemoryUsed
3✔
283
        }
3✔
284
        for _, id := range a.freeTables {
14✔
285
                table := &storage.tables[id]
3✔
286
                cap += int(table.cap)
3✔
287
                memory += stats.MemoryPerEntity * int(table.cap)
3✔
288
        }
3✔
289

290
        stats.FreeTables = len(a.freeTables)
11✔
291
        stats.Capacity = cap
11✔
292
        stats.Size = count
11✔
293
        stats.Memory = memory
11✔
294
        stats.MemoryUsed = memoryUsed
11✔
295
}
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