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

mlange-42 / arche / 7502900423

12 Jan 2024 01:35PM CUT coverage: 100.0%. Remained the same
7502900423

push

github

web-flow
Component ID getters in world and query copy ID slice (#325)

Both return a copy of the archetype's component IDs slice, for safety.
This means that the result can be manipulated safely,
but also that calling the method may incur some significant cost.

4814 of 4814 relevant lines covered (100.0%)

66181.46 hits per line

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

100.0
/ecs/archetype_node.go
1
package ecs
2

3
import (
4
        "reflect"
5
        "unsafe"
6

7
        "github.com/mlange-42/arche/ecs/stats"
8
)
9

10
// archNode is a node in the archetype graph.
11
type archNode struct {
12
        *nodeData
13
        Mask        Mask // Mask of the archetype
14
        Relation    int8 // The node's relation component ID. Negative value stands for no relation
15
        IsActive    bool
16
        HasRelation bool
17
}
18

19
type nodeData struct {
20
        Ids               []ID                  // List of component IDs
21
        Types             []reflect.Type        // Component type per column
22
        TransitionAdd     idMap[*archNode]      // Mapping from component ID to add to the resulting archetype
23
        TransitionRemove  idMap[*archNode]      // Mapping from component ID to remove to the resulting archetype
24
        archetype         *archetype            // The single archetype for nodes without entity relation
25
        archetypes        pagedSlice[archetype] // Storage for archetypes in nodes with entity relation
26
        archetypeData     pagedSlice[archetypeData]
27
        archetypeMap      map[Entity]*archetype // Mapping from relation targets to archetypes
28
        freeIndices       []int32               // Indices of free/inactive archetypes
29
        zeroValue         []byte                // Used as source for setting storage to zero
30
        zeroPointer       unsafe.Pointer        // Points to zeroValue for fast access
31
        capacityIncrement uint32                // Capacity increment
32
}
33

34
// Creates a new archNode
35
func newArchNode(mask Mask, data *nodeData, relation int8, capacityIncrement int, components []componentType) archNode {
1,340✔
36
        var arch map[Entity]*archetype
1,340✔
37
        if relation >= 0 {
1,379✔
38
                arch = map[Entity]*archetype{}
39✔
39
        }
39✔
40
        ids := make([]ID, len(components))
1,340✔
41
        types := make([]reflect.Type, len(components))
1,340✔
42

1,340✔
43
        var maxSize uintptr = 0
1,340✔
44
        prev := -1
1,340✔
45
        for i, c := range components {
6,741✔
46
                if int(c.ID) <= prev {
5,402✔
47
                        panic("component arguments must be sorted by ID")
1✔
48
                }
49
                prev = int(c.ID)
5,400✔
50

5,400✔
51
                ids[i] = c.ID
5,400✔
52
                types[i] = c.Type
5,400✔
53
                size, align := c.Type.Size(), uintptr(c.Type.Align())
5,400✔
54
                size = (size + (align - 1)) / align * align
5,400✔
55
                if size > maxSize {
6,595✔
56
                        maxSize = size
1,195✔
57
                }
1,195✔
58
        }
59

60
        var zeroValue []byte
1,339✔
61
        var zeroPointer unsafe.Pointer
1,339✔
62
        if maxSize > 0 {
2,532✔
63
                zeroValue = make([]byte, maxSize)
1,193✔
64
                zeroPointer = unsafe.Pointer(&zeroValue[0])
1,193✔
65
        }
1,193✔
66

67
        data.Ids = ids
1,339✔
68
        data.Types = types
1,339✔
69
        data.archetypeMap = arch
1,339✔
70
        data.capacityIncrement = uint32(capacityIncrement)
1,339✔
71
        data.zeroValue = zeroValue
1,339✔
72
        data.zeroPointer = zeroPointer
1,339✔
73
        data.TransitionAdd = newIDMap[*archNode]()
1,339✔
74
        data.TransitionRemove = newIDMap[*archNode]()
1,339✔
75

1,339✔
76
        return archNode{
1,339✔
77
                nodeData:    data,
1,339✔
78
                Mask:        mask,
1,339✔
79
                Relation:    relation,
1,339✔
80
                HasRelation: relation >= 0,
1,339✔
81
        }
1,339✔
82
}
83

84
// Matches the archetype node against a filter.
85
// Ignores the relation target.
86
func (a *archNode) Matches(f Filter) bool {
77,053✔
87
        return f.Matches(&a.Mask)
77,053✔
88
}
77,053✔
89

90
// Archetypes of the node.
91
// Returns a single wrapped archetype if there are no relations.
92
// Returns nil if the node has no archetype(s).
93
func (a *archNode) Archetypes() archetypes {
150,741✔
94
        if a.archetype == nil {
200,811✔
95
                return &a.archetypes
50,070✔
96
        }
50,070✔
97
        return singleArchetype{Archetype: a.archetype}
100,671✔
98
}
99

100
// GetArchetype returns the archetype for the given relation target.
101
//
102
// The target is ignored if the node has no relation component.
103
func (a *archNode) GetArchetype(target Entity) *archetype {
60,813✔
104
        if a.Relation >= 0 {
93,839✔
105
                return a.archetypeMap[target]
33,026✔
106
        }
33,026✔
107
        return a.archetype
27,787✔
108
}
109

110
// SetArchetype sets the archetype for a node without a relation.
111
//
112
// Do not use on nodes without a relation component!
113
func (a *archNode) SetArchetype(arch *archetype) {
1,259✔
114
        a.archetype = arch
1,259✔
115
}
1,259✔
116

117
// CreateArchetype creates a new archetype in nodes with relation component.
118
func (a *archNode) CreateArchetype(target Entity) *archetype {
27,596✔
119
        var arch *archetype
27,596✔
120
        var archIndex int32
27,596✔
121
        lenFree := len(a.freeIndices)
27,596✔
122
        if lenFree > 0 {
55,000✔
123
                archIndex = a.freeIndices[lenFree-1]
27,404✔
124
                arch = a.archetypes.Get(archIndex)
27,404✔
125
                a.freeIndices = a.freeIndices[:lenFree-1]
27,404✔
126
                arch.Activate(target, archIndex)
27,404✔
127
        } else {
27,596✔
128
                a.archetypes.Add(archetype{})
192✔
129
                a.archetypeData.Add(archetypeData{})
192✔
130
                archIndex := a.archetypes.Len() - 1
192✔
131
                arch = a.archetypes.Get(archIndex)
192✔
132
                arch.Init(a, a.archetypeData.Get(archIndex), archIndex, true, target)
192✔
133
        }
192✔
134
        a.archetypeMap[target] = arch
27,596✔
135
        return arch
27,596✔
136
}
137

138
// RemoveArchetype de-activates an archetype.
139
// The archetype will be re-used by CreateArchetype.
140
func (a *archNode) RemoveArchetype(arch *archetype) {
27,427✔
141
        delete(a.archetypeMap, arch.RelationTarget)
27,427✔
142
        idx := arch.index
27,427✔
143
        a.freeIndices = append(a.freeIndices, idx)
27,427✔
144
        a.archetypes.Get(idx).Deactivate()
27,427✔
145
}
27,427✔
146

147
// Reset resets the archetypes in this node.
148
// Relation archetypes with non-zero target are de-activated for re-use.
149
func (a *archNode) Reset(cache *Cache) {
97✔
150
        if !a.IsActive {
102✔
151
                return
5✔
152
        }
5✔
153
        if !a.HasRelation {
156✔
154
                a.archetype.Reset()
64✔
155
                return
64✔
156
        }
64✔
157

158
        lenArches := a.archetypes.Len()
28✔
159
        var j int32
28✔
160
        for j = 0; j < lenArches; j++ {
2,439✔
161
                arch := a.archetypes.Get(j)
2,411✔
162
                if !arch.IsActive() {
2,413✔
163
                        continue
2✔
164
                }
165
                if !arch.RelationTarget.IsZero() {
4,816✔
166
                        a.RemoveArchetype(arch)
2,407✔
167
                        cache.removeArchetype(arch)
2,407✔
168
                } else {
2,409✔
169
                        arch.Reset()
2✔
170
                }
2✔
171
        }
172
}
173

174
// Stats generates statistics for an archetype node.
175
func (a *archNode) Stats(reg *componentRegistry[ID]) stats.NodeStats {
13✔
176
        ids := a.Ids
13✔
177
        aCompCount := len(ids)
13✔
178
        aTypes := make([]reflect.Type, aCompCount)
13✔
179
        for j, id := range ids {
24✔
180
                aTypes[j], _ = reg.ComponentType(id)
11✔
181
        }
11✔
182

183
        arches := a.Archetypes()
13✔
184
        var numArches int32
13✔
185
        cap := 0
13✔
186
        count := 0
13✔
187
        memory := 0
13✔
188
        var archStats []stats.ArchetypeStats
13✔
189
        if arches != nil {
26✔
190
                numArches = arches.Len()
13✔
191
                archStats = make([]stats.ArchetypeStats, numArches)
13✔
192
                var i int32
13✔
193
                for i = 0; i < numArches; i++ {
125✔
194
                        archStats[i] = arches.Get(i).Stats(reg)
112✔
195
                        stats := &archStats[i]
112✔
196
                        cap += stats.Capacity
112✔
197
                        count += stats.Size
112✔
198
                        memory += stats.Memory
112✔
199
                }
112✔
200
        }
201

202
        memPerEntity := 0
13✔
203
        for j := range ids {
24✔
204
                memPerEntity += int(aTypes[j].Size())
11✔
205
        }
11✔
206

207
        return stats.NodeStats{
13✔
208
                ArchetypeCount:       int(numArches),
13✔
209
                ActiveArchetypeCount: int(numArches) - len(a.freeIndices),
13✔
210
                IsActive:             a.IsActive,
13✔
211
                HasRelation:          a.HasRelation,
13✔
212
                Components:           aCompCount,
13✔
213
                ComponentIDs:         ids,
13✔
214
                ComponentTypes:       aTypes,
13✔
215
                Memory:               memory,
13✔
216
                MemoryPerEntity:      memPerEntity,
13✔
217
                Size:                 count,
13✔
218
                Capacity:             cap,
13✔
219
                Archetypes:           archStats,
13✔
220
        }
13✔
221
}
222

223
// UpdateStats updates statistics for an archetype node.
224
func (a *archNode) UpdateStats(stats *stats.NodeStats, reg *componentRegistry[ID]) {
150,022✔
225
        if !a.IsActive {
150,023✔
226
                return
1✔
227
        }
1✔
228

229
        arches := a.Archetypes()
150,021✔
230

150,021✔
231
        if !stats.IsActive {
150,022✔
232
                temp := a.Stats(reg)
1✔
233
                *stats = temp
1✔
234
                return
1✔
235
        }
1✔
236

237
        cap := 0
150,020✔
238
        count := 0
150,020✔
239
        memory := 0
150,020✔
240

150,020✔
241
        cntOld := int32(len(stats.Archetypes))
150,020✔
242
        cntNew := int32(arches.Len())
150,020✔
243
        var i int32
150,020✔
244
        for i = 0; i < cntOld; i++ {
5,249,944✔
245
                arch := &stats.Archetypes[i]
5,099,924✔
246
                arches.Get(i).UpdateStats(stats, arch, reg)
5,099,924✔
247
                cap += arch.Capacity
5,099,924✔
248
                count += arch.Size
5,099,924✔
249
                memory += arch.Memory
5,099,924✔
250
        }
5,099,924✔
251
        for i = cntOld; i < cntNew; i++ {
150,021✔
252
                arch := arches.Get(i).Stats(reg)
1✔
253
                stats.Archetypes = append(stats.Archetypes, arch)
1✔
254
                cap += arch.Capacity
1✔
255
                count += arch.Size
1✔
256
                memory += arch.Memory
1✔
257
        }
1✔
258

259
        stats.IsActive = true
150,020✔
260
        stats.ArchetypeCount = int(cntNew)
150,020✔
261
        stats.ActiveArchetypeCount = int(cntNew) - len(a.freeIndices)
150,020✔
262
        stats.Capacity = cap
150,020✔
263
        stats.Size = count
150,020✔
264
        stats.Memory = memory
150,020✔
265
}
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