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

mlange-42 / arche / 7775301742

04 Feb 2024 04:17PM CUT coverage: 100.0%. Remained the same
7775301742

push

github

web-flow
Fix Zenodo DOI in README.md (#360)

6159 of 6159 relevant lines covered (100.0%)

52100.66 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    ID
15
        HasRelation bool
16
        IsActive    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 ID, hasRelation bool, capacityIncrement int, components []componentType) archNode {
1,370✔
36
        var arch map[Entity]*archetype
1,370✔
37
        if hasRelation {
1,420✔
38
                arch = map[Entity]*archetype{}
50✔
39
        }
50✔
40
        ids := make([]ID, len(components))
1,370✔
41
        types := make([]reflect.Type, len(components))
1,370✔
42

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

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

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

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

1,369✔
76
        return archNode{
1,369✔
77
                nodeData:    data,
1,369✔
78
                Mask:        mask,
1,369✔
79
                Relation:    relation,
1,369✔
80
                HasRelation: hasRelation,
1,369✔
81
        }
1,369✔
82
}
83

84
// Matches the archetype node against a filter.
85
// Ignores the relation target.
86
func (a *archNode) Matches(f Filter) bool {
77,097✔
87
        return f.Matches(&a.Mask)
77,097✔
88
}
77,097✔
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,757✔
94
        if a.archetype == nil {
200,841✔
95
                return &a.archetypes
50,084✔
96
        }
50,084✔
97
        return singleArchetype{Archetype: a.archetype}
100,673✔
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,846✔
104
        if a.HasRelation {
93,897✔
105
                return a.archetypeMap[target]
33,051✔
106
        }
33,051✔
107
        return a.archetype
27,795✔
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,278✔
114
        a.archetype = arch
1,278✔
115
}
1,278✔
116

117
// CreateArchetype creates a new archetype in nodes with relation component.
118
func (a *archNode) CreateArchetype(layouts uint8, target Entity) *archetype {
27,610✔
119
        var arch *archetype
27,610✔
120
        var archIndex int32
27,610✔
121
        lenFree := len(a.freeIndices)
27,610✔
122
        if lenFree > 0 {
55,014✔
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,610✔
128
                a.archetypes.Add(archetype{})
206✔
129
                a.archetypeData.Add(archetypeData{})
206✔
130
                archIndex := a.archetypes.Len() - 1
206✔
131
                arch = a.archetypes.Get(archIndex)
206✔
132
                arch.Init(a, a.archetypeData.Get(archIndex), archIndex, true, layouts, target)
206✔
133
        }
206✔
134
        a.archetypeMap[target] = arch
27,610✔
135
        return arch
27,610✔
136
}
137

138
func (a *archNode) ExtendArchetypeLayouts(count uint8) {
6✔
139
        if !a.HasRelation {
10✔
140
                a.archetype.ExtendLayouts(count)
4✔
141
                return
4✔
142
        }
4✔
143

144
        lenArches := a.archetypes.Len()
2✔
145
        var j int32
2✔
146
        for j = 0; j < lenArches; j++ {
4✔
147
                arch := a.archetypes.Get(j)
2✔
148
                arch.ExtendLayouts(count)
2✔
149
        }
2✔
150
}
151

152
// RemoveArchetype de-activates an archetype.
153
// The archetype will be re-used by CreateArchetype.
154
func (a *archNode) RemoveArchetype(arch *archetype) {
27,427✔
155
        delete(a.archetypeMap, arch.RelationTarget)
27,427✔
156
        idx := arch.index
27,427✔
157
        a.freeIndices = append(a.freeIndices, idx)
27,427✔
158
        a.archetypes.Get(idx).Deactivate()
27,427✔
159
}
27,427✔
160

161
// Reset resets the archetypes in this node.
162
// Relation archetypes with non-zero target are de-activated for re-use.
163
func (a *archNode) Reset(cache *Cache) {
97✔
164
        if !a.IsActive {
102✔
165
                return
5✔
166
        }
5✔
167
        if !a.HasRelation {
156✔
168
                a.archetype.Reset()
64✔
169
                return
64✔
170
        }
64✔
171

172
        lenArches := a.archetypes.Len()
28✔
173
        var j int32
28✔
174
        for j = 0; j < lenArches; j++ {
2,439✔
175
                arch := a.archetypes.Get(j)
2,411✔
176
                if !arch.IsActive() {
2,413✔
177
                        continue
2✔
178
                }
179
                if !arch.RelationTarget.IsZero() {
4,816✔
180
                        a.RemoveArchetype(arch)
2,407✔
181
                        cache.removeArchetype(arch)
2,407✔
182
                } else {
2,409✔
183
                        arch.Reset()
2✔
184
                }
2✔
185
        }
186
}
187

188
// Stats generates statistics for an archetype node.
189
func (a *archNode) Stats(reg *componentRegistry) stats.NodeStats {
13✔
190
        ids := a.Ids
13✔
191
        aCompCount := len(ids)
13✔
192
        aTypes := make([]reflect.Type, aCompCount)
13✔
193
        for j, id := range ids {
24✔
194
                aTypes[j], _ = reg.ComponentType(id.id)
11✔
195
        }
11✔
196

197
        arches := a.Archetypes()
13✔
198
        var numArches int32
13✔
199
        cap := 0
13✔
200
        count := 0
13✔
201
        memory := 0
13✔
202
        var archStats []stats.ArchetypeStats
13✔
203
        if arches != nil {
26✔
204
                numArches = arches.Len()
13✔
205
                archStats = make([]stats.ArchetypeStats, numArches)
13✔
206
                var i int32
13✔
207
                for i = 0; i < numArches; i++ {
125✔
208
                        archStats[i] = arches.Get(i).Stats(reg)
112✔
209
                        stats := &archStats[i]
112✔
210
                        cap += stats.Capacity
112✔
211
                        count += stats.Size
112✔
212
                        memory += stats.Memory
112✔
213
                }
112✔
214
        }
215

216
        memPerEntity := 0
13✔
217
        intIDs := make([]uint8, len(ids))
13✔
218
        for j, id := range ids {
24✔
219
                intIDs[j] = id.id
11✔
220
                memPerEntity += int(aTypes[j].Size())
11✔
221
        }
11✔
222

223
        return stats.NodeStats{
13✔
224
                ArchetypeCount:       int(numArches),
13✔
225
                ActiveArchetypeCount: int(numArches) - len(a.freeIndices),
13✔
226
                IsActive:             a.IsActive,
13✔
227
                HasRelation:          a.HasRelation,
13✔
228
                Components:           aCompCount,
13✔
229
                ComponentIDs:         intIDs,
13✔
230
                ComponentTypes:       aTypes,
13✔
231
                Memory:               memory,
13✔
232
                MemoryPerEntity:      memPerEntity,
13✔
233
                Size:                 count,
13✔
234
                Capacity:             cap,
13✔
235
                Archetypes:           archStats,
13✔
236
        }
13✔
237
}
238

239
// UpdateStats updates statistics for an archetype node.
240
func (a *archNode) UpdateStats(stats *stats.NodeStats, reg *componentRegistry) {
150,022✔
241
        if !a.IsActive {
150,023✔
242
                return
1✔
243
        }
1✔
244

245
        arches := a.Archetypes()
150,021✔
246

150,021✔
247
        if !stats.IsActive {
150,022✔
248
                temp := a.Stats(reg)
1✔
249
                *stats = temp
1✔
250
                return
1✔
251
        }
1✔
252

253
        cap := 0
150,020✔
254
        count := 0
150,020✔
255
        memory := 0
150,020✔
256

150,020✔
257
        cntOld := int32(len(stats.Archetypes))
150,020✔
258
        cntNew := int32(arches.Len())
150,020✔
259
        var i int32
150,020✔
260
        for i = 0; i < cntOld; i++ {
5,249,944✔
261
                arch := &stats.Archetypes[i]
5,099,924✔
262
                arches.Get(i).UpdateStats(stats, arch, reg)
5,099,924✔
263
                cap += arch.Capacity
5,099,924✔
264
                count += arch.Size
5,099,924✔
265
                memory += arch.Memory
5,099,924✔
266
        }
5,099,924✔
267
        for i = cntOld; i < cntNew; i++ {
150,021✔
268
                arch := arches.Get(i).Stats(reg)
1✔
269
                stats.Archetypes = append(stats.Archetypes, arch)
1✔
270
                cap += arch.Capacity
1✔
271
                count += arch.Size
1✔
272
                memory += arch.Memory
1✔
273
        }
1✔
274

275
        stats.IsActive = true
150,020✔
276
        stats.ArchetypeCount = int(cntNew)
150,020✔
277
        stats.ActiveArchetypeCount = int(cntNew) - len(a.freeIndices)
150,020✔
278
        stats.Capacity = cap
150,020✔
279
        stats.Size = count
150,020✔
280
        stats.Memory = memory
150,020✔
281
}
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