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

mlange-42 / ark / 13705674638

06 Mar 2025 06:31PM CUT coverage: 99.41% (+0.03%) from 99.379%
13705674638

Pull #147

github

web-flow
Merge becb7e527 into a5c169bcb
Pull Request #147: Optimize getting table from archetype

2 of 2 new or added lines in 2 files covered. (100.0%)

6237 of 6274 relevant lines covered (99.41%)

28634.75 hits per line

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

99.15
/ecs/archetype.go
1
package ecs
2

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

8
type archetypeID uint32
9

10
// maxArchetypeID is used as unassigned archetype ID.
11
const maxArchetypeID = math.MaxUint32
12

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

26
type tableIDs struct {
27
        tables []tableID
28
}
29

30
func newArchetype(id archetypeID, node nodeID, mask *Mask, components []ID, tables []tableID, reg *componentRegistry) archetype {
589✔
31
        componentsMap := make([]int16, MaskTotalBits)
589✔
32
        for i := range MaskTotalBits {
151,373✔
33
                componentsMap[i] = -1
150,784✔
34
        }
150,784✔
35
        for i, id := range components {
2,010✔
36
                componentsMap[id.id] = int16(i)
1,421✔
37
        }
1,421✔
38

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

62
func (a *archetype) HasRelations() bool {
497,586✔
63
        return a.numRelations > 0
497,586✔
64
}
497,586✔
65

66
func (a *archetype) GetTable(storage *storage, relations []RelationID) (*table, bool) {
495,036✔
67
        if len(a.tables) == 0 {
495,429✔
68
                return nil, false
393✔
69
        }
393✔
70
        if !a.HasRelations() {
988,622✔
71
                return &storage.tables[a.tables[0]], true
493,979✔
72
        }
493,979✔
73
        index := a.componentsMap[relations[0].component.id]
664✔
74
        tables, ok := a.relationTables[index][relations[0].target.id]
664✔
75
        if !ok {
740✔
76
                return nil, false
76✔
77
        }
76✔
78
        for _, t := range tables.tables {
1,195✔
79
                table := &storage.tables[t]
607✔
80
                if table.MatchesExact(relations) {
1,193✔
81
                        return table, true
586✔
82
                }
586✔
83
        }
84
        return nil, false
2✔
85
}
86

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

98
func (a *archetype) GetFreeTable() (tableID, bool) {
471✔
99
        if len(a.freeTables) == 0 {
940✔
100
                return 0, false
469✔
101
        }
469✔
102
        last := len(a.freeTables) - 1
2✔
103
        table := a.freeTables[last]
2✔
104

2✔
105
        a.freeTables = a.freeTables[:last]
2✔
106

2✔
107
        return table, true
2✔
108
}
109

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

10✔
115
        if index != last {
14✔
116
                a.tables[index], a.tables[last] = a.tables[last], a.tables[index]
4✔
117
        }
4✔
118
        a.tables = a.tables[:last]
10✔
119

10✔
120
        a.freeTables = append(a.freeTables, table)
10✔
121
}
122

123
func (a *archetype) AddTable(table *table) {
471✔
124
        a.tables = append(a.tables, table.id)
471✔
125
        if !a.HasRelations() {
815✔
126
                return
344✔
127
        }
344✔
128

129
        for i := range table.ids {
599✔
130
                column := &table.columns[i]
472✔
131
                if !column.isRelation {
809✔
132
                        continue
337✔
133
                }
134
                target := column.target
135✔
135
                relations := a.relationTables[i]
135✔
136

135✔
137
                if tables, ok := relations[target.id]; ok {
140✔
138
                        tables.tables = append(tables.tables, table.id)
5✔
139
                } else {
135✔
140
                        relations[target.id] = &tableIDs{tables: []tableID{table.id}}
130✔
141
                }
130✔
142
        }
143
}
144

145
func (a *archetype) RemoveTarget(entity Entity) {
13✔
146
        for i := range a.relationTables {
31✔
147
                if !a.isRelation[i] {
23✔
148
                        continue
5✔
149
                }
150
                delete(a.relationTables[i], entity.id)
13✔
151
        }
152
}
153

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

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

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

170
        for _, m := range a.relationTables {
3✔
171
                for key := range m {
3✔
172
                        delete(m, key)
1✔
173
                }
1✔
174
        }
175
}
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