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

mlange-42 / arche / 4874413404

03 May 2023 04:46PM CUT coverage: 100.0%. Remained the same
4874413404

push

github

GitHub
Batch-set entity relation, RelationFilter to function (#265)

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

3764 of 3764 relevant lines covered (100.0%)

97404.29 hits per line

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

100.0
/ecs/archetype.go
1
package ecs
2

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

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

11
// layoutSize is the size of an archetype column layout in bytes.
12
var layoutSize = unsafe.Sizeof(layout{})
13

14
// archetypeNode is a node in the archetype graph.
15
type archetypeNode struct {
16
        mask              Mask                  // Mask of the archetype
17
        Ids               []ID                  // List of component IDs
18
        archetype         *archetype            // The single archetype for nodes without entity
19
        archetypes        pagedSlice[archetype] // Storage for archetypes in nodes with entity relation
20
        archetypeMap      map[Entity]*archetype // Mapping from relation targets to archetypes
21
        freeIndices       []int32               // Indices of free/inactive archetypes
22
        TransitionAdd     idMap[*archetypeNode] // Mapping from component ID to add to the resulting archetype
23
        TransitionRemove  idMap[*archetypeNode] // Mapping from component ID to remove to the resulting archetype
24
        relation          int8                  // The node's relation component ID. Negative value stands for no relation
25
        zeroValue         []byte                // Used as source for setting storage to zero
26
        zeroPointer       unsafe.Pointer        // Points to zeroValue for fast access
27
        capacityIncrement uint32                // Capacity increment
28
}
29

30
// Creates a new archetypeNode
31
func newArchetypeNode(mask Mask, relation int8, capacityIncrement int) archetypeNode {
1,243✔
32
        var arch map[Entity]*archetype
1,243✔
33
        if relation >= 0 {
1,268✔
34
                arch = map[Entity]*archetype{}
25✔
35
        }
25✔
36
        return archetypeNode{
1,243✔
37
                mask:              mask,
1,243✔
38
                archetypeMap:      arch,
1,243✔
39
                TransitionAdd:     newIDMap[*archetypeNode](),
1,243✔
40
                TransitionRemove:  newIDMap[*archetypeNode](),
1,243✔
41
                relation:          relation,
1,243✔
42
                capacityIncrement: uint32(capacityIncrement),
1,243✔
43
        }
1,243✔
44
}
45

46
// Matches the archetype node against a filter.
47
// Ignores the relation target.
48
func (a *archetypeNode) Matches(f Filter) bool {
76,722✔
49
        return f.Matches(a.mask, nil)
76,722✔
50
}
76,722✔
51

52
// Archetypes of the node.
53
// Returns a single wrapped archetype if there are no relations.
54
// Returns nil if the node has no archetype(s).
55
func (a *archetypeNode) Archetypes() archetypes {
176,843✔
56
        if a.HasRelation() {
251,968✔
57
                return &a.archetypes
75,125✔
58
        }
75,125✔
59
        if a.archetype == nil {
101,858✔
60
                return nil
140✔
61
        }
140✔
62
        return singleArchetype{Archetype: a.archetype}
101,578✔
63
}
64

65
// GetArchetype returns the archetype for the given relation target.
66
//
67
// The target is ignored if the node has no relation component.
68
func (a *archetypeNode) GetArchetype(target Entity) *archetype {
60,753✔
69
        if a.relation >= 0 {
93,754✔
70
                return a.archetypeMap[target]
33,001✔
71
        }
33,001✔
72
        return a.archetype
27,752✔
73
}
74

75
// SetArchetype sets the archetype for a node without a relation.
76
//
77
// Do not use on nodes without a relation component!
78
func (a *archetypeNode) SetArchetype(arch *archetype) {
1,187✔
79
        a.archetype = arch
1,187✔
80
}
1,187✔
81

82
// CreateArchetype creates a new archetype in nodes with relation component.
83
func (a *archetypeNode) CreateArchetype(target Entity, components ...componentType) *archetype {
27,571✔
84
        var arch *archetype
27,571✔
85
        var archIndex int32
27,571✔
86
        lenFree := len(a.freeIndices)
27,571✔
87
        if lenFree > 0 {
54,973✔
88
                archIndex = a.freeIndices[lenFree-1]
27,402✔
89
                arch = a.archetypes.Get(archIndex)
27,402✔
90
                a.freeIndices = a.freeIndices[:lenFree-1]
27,402✔
91
                arch.Activate(target, archIndex)
27,402✔
92
        } else {
27,571✔
93
                a.archetypes.Add(archetype{})
169✔
94
                archIndex := a.archetypes.Len() - 1
169✔
95
                arch = a.archetypes.Get(archIndex)
169✔
96
                arch.Init(a, archIndex, true, target, components...)
169✔
97
        }
169✔
98
        a.archetypeMap[target] = arch
27,571✔
99
        return arch
27,571✔
100
}
101

102
// RemoveArchetype de-activates an archetype.
103
// The archetype will be re-used by CreateArchetype.
104
func (a *archetypeNode) RemoveArchetype(arch *archetype) {
27,413✔
105
        delete(a.archetypeMap, arch.Relation)
27,413✔
106
        idx := arch.index
27,413✔
107
        a.freeIndices = append(a.freeIndices, idx)
27,413✔
108
        a.archetypes.Get(idx).Deactivate()
27,413✔
109
}
27,413✔
110

111
// HasRelation returns whether the node has a relation component.
112
func (a *archetypeNode) HasRelation() bool {
234,326✔
113
        return a.relation >= 0
234,326✔
114
}
234,326✔
115

116
// IsActive returns whether the node is active, i.e. has archetypes.
117
func (a *archetypeNode) IsActive() bool {
376,801✔
118
        return a.Ids != nil
376,801✔
119
}
376,801✔
120

121
// Stats generates statistics for an archetype node.
122
func (a *archetypeNode) Stats(reg *componentRegistry[ID]) stats.NodeStats {
13✔
123
        ids := a.Ids
13✔
124
        aCompCount := len(ids)
13✔
125
        aTypes := make([]reflect.Type, aCompCount)
13✔
126
        for j, id := range ids {
22✔
127
                aTypes[j], _ = reg.ComponentType(id)
9✔
128
        }
9✔
129

130
        arches := a.Archetypes()
13✔
131
        var numArches int32
13✔
132
        cap := 0
13✔
133
        count := 0
13✔
134
        memory := 0
13✔
135
        var archStats []stats.ArchetypeStats
13✔
136
        if arches != nil {
25✔
137
                numArches = arches.Len()
12✔
138
                archStats = make([]stats.ArchetypeStats, numArches)
12✔
139
                var i int32
12✔
140
                for i = 0; i < numArches; i++ {
124✔
141
                        archStats[i] = arches.Get(i).Stats(reg)
112✔
142
                        stats := &archStats[i]
112✔
143
                        cap += stats.Capacity
112✔
144
                        count += stats.Size
112✔
145
                        memory += stats.Memory
112✔
146
                }
112✔
147
        }
148

149
        memPerEntity := 0
13✔
150
        for j := range ids {
22✔
151
                memPerEntity += int(aTypes[j].Size())
9✔
152
        }
9✔
153

154
        return stats.NodeStats{
13✔
155
                ArchetypeCount:       int(numArches),
13✔
156
                ActiveArchetypeCount: int(numArches) - len(a.freeIndices),
13✔
157
                IsActive:             a.IsActive(),
13✔
158
                HasRelation:          a.HasRelation(),
13✔
159
                Components:           aCompCount,
13✔
160
                ComponentIDs:         ids,
13✔
161
                ComponentTypes:       aTypes,
13✔
162
                Memory:               memory,
13✔
163
                MemoryPerEntity:      memPerEntity,
13✔
164
                Size:                 count,
13✔
165
                Capacity:             cap,
13✔
166
                Archetypes:           archStats,
13✔
167
        }
13✔
168
}
169

170
// UpdateStats updates statistics for an archetype node.
171
func (a *archetypeNode) UpdateStats(stats *stats.NodeStats, reg *componentRegistry[ID]) {
150,022✔
172
        if !a.IsActive() {
150,023✔
173
                return
1✔
174
        }
1✔
175

176
        arches := a.Archetypes()
150,021✔
177

150,021✔
178
        if !stats.IsActive {
150,022✔
179
                temp := a.Stats(reg)
1✔
180
                *stats = temp
1✔
181
                return
1✔
182
        }
1✔
183

184
        cap := 0
150,020✔
185
        count := 0
150,020✔
186
        memory := 0
150,020✔
187

150,020✔
188
        cntOld := int32(len(stats.Archetypes))
150,020✔
189
        cntNew := int32(arches.Len())
150,020✔
190
        var i int32
150,020✔
191
        for i = 0; i < cntOld; i++ {
5,249,944✔
192
                arch := &stats.Archetypes[i]
5,099,924✔
193
                arches.Get(i).UpdateStats(stats, arch, reg)
5,099,924✔
194
                cap += arch.Capacity
5,099,924✔
195
                count += arch.Size
5,099,924✔
196
                memory += arch.Memory
5,099,924✔
197
        }
5,099,924✔
198
        for i = cntOld; i < cntNew; i++ {
150,021✔
199
                arch := arches.Get(i).Stats(reg)
1✔
200
                stats.Archetypes = append(stats.Archetypes, arch)
1✔
201
                cap += arch.Capacity
1✔
202
                count += arch.Size
1✔
203
                memory += arch.Memory
1✔
204
        }
1✔
205

206
        stats.IsActive = true
150,020✔
207
        stats.ArchetypeCount = int(cntNew)
150,020✔
208
        stats.ActiveArchetypeCount = int(cntNew) - len(a.freeIndices)
150,020✔
209
        stats.Capacity = cap
150,020✔
210
        stats.Size = count
150,020✔
211
        stats.Memory = memory
150,020✔
212
}
213

214
// Helper for accessing data from an archetype
215
type archetypeAccess struct {
216
        Mask              Mask           // Archetype's mask
217
        basePointer       unsafe.Pointer // Pointer to the first component column layout.
218
        entityPointer     unsafe.Pointer // Pointer to the entity storage
219
        Relation          Entity
220
        RelationComponent int8
221
}
222

223
// Matches checks if the archetype matches the given mask.
224
func (a *archetype) Matches(f Filter) bool {
2,500,544✔
225
        return f.Matches(a.Mask, &a.Relation)
2,500,544✔
226
}
2,500,544✔
227

228
// GetEntity returns the entity at the given index
229
func (a *archetypeAccess) GetEntity(index uintptr) Entity {
2,534,020✔
230
        return *(*Entity)(unsafe.Add(a.entityPointer, entitySize*index))
2,534,020✔
231
}
2,534,020✔
232

233
// Get returns the component with the given ID at the given index
234
func (a *archetypeAccess) Get(index uintptr, id ID) unsafe.Pointer {
23,505✔
235
        return a.getLayout(id).Get(index)
23,505✔
236
}
23,505✔
237

238
// GetEntity returns the entity at the given index
239
func (a *archetypeAccess) GetRelation() Entity {
823✔
240
        return a.Relation
823✔
241
}
823✔
242

243
// HasComponent returns whether the archetype contains the given component ID.
244
func (a *archetypeAccess) HasComponent(id ID) bool {
9,702✔
245
        return a.getLayout(id).pointer != nil
9,702✔
246
}
9,702✔
247

248
// HasRelation returns whether the archetype has a relation component.
249
func (a *archetypeAccess) HasRelation() bool {
2,461✔
250
        return a.RelationComponent >= 0
2,461✔
251
}
2,461✔
252

253
// GetLayout returns the column layout for a component.
254
func (a *archetypeAccess) getLayout(id ID) *layout {
94,172✔
255
        return (*layout)(unsafe.Add(a.basePointer, layoutSize*uintptr(id)))
94,172✔
256
}
94,172✔
257

258
// layout specification of a component column.
259
type layout struct {
260
        pointer  unsafe.Pointer // Pointer to the first element in the component column.
261
        itemSize uintptr        // Component/step size
262
}
263

264
// Get returns a pointer to the item at the given index.
265
func (l *layout) Get(index uintptr) unsafe.Pointer {
23,503✔
266
        if l.pointer == nil {
23,504✔
267
                return nil
1✔
268
        }
1✔
269
        return unsafe.Add(l.pointer, l.itemSize*index)
23,502✔
270
}
271

272
// archetype represents an ECS archetype
273
type archetype struct {
274
        archetypeAccess                 // Access helper, passed to queries.
275
        graphNode       *archetypeNode  // Node in the archetype graph.
276
        layouts         []layout        // Column layouts by ID.
277
        indices         idMap[uint32]   // Mapping from IDs to buffer indices.
278
        buffers         []reflect.Value // Reflection arrays containing component data.
279
        entityBuffer    reflect.Value   // Reflection array containing entity data.
280
        index           int32           // Index of the archetype in the world.
281
        len             uint32          // Current number of entities
282
        cap             uint32          // Current capacity
283
}
284

285
// Init initializes an archetype
286
func (a *archetype) Init(node *archetypeNode, index int32, forStorage bool, relation Entity, components ...componentType) {
1,365✔
287
        var mask Mask
1,365✔
288
        if !node.IsActive() {
2,581✔
289
                node.Ids = make([]ID, len(components))
1,216✔
290

1,216✔
291
                var maxSize uintptr = 0
1,216✔
292
                for i, c := range components {
6,506✔
293
                        node.Ids[i] = c.ID
5,290✔
294
                        size, align := c.Type.Size(), uintptr(c.Type.Align())
5,290✔
295
                        size = (size + (align - 1)) / align * align
5,290✔
296
                        if size > maxSize {
6,415✔
297
                                maxSize = size
1,125✔
298
                        }
1,125✔
299
                }
300

301
                if maxSize > 0 {
2,339✔
302
                        node.zeroValue = make([]byte, maxSize)
1,123✔
303
                        node.zeroPointer = unsafe.Pointer(&node.zeroValue[0])
1,123✔
304
                }
1,123✔
305
        }
306

307
        a.buffers = make([]reflect.Value, len(components))
1,365✔
308
        a.layouts = make([]layout, MaskTotalBits)
1,365✔
309
        a.indices = newIDMap[uint32]()
1,365✔
310
        a.index = index
1,365✔
311

1,365✔
312
        cap := 1
1,365✔
313
        if forStorage {
2,643✔
314
                cap = int(node.capacityIncrement)
1,278✔
315
        }
1,278✔
316

317
        prev := -1
1,365✔
318
        for i, c := range components {
6,821✔
319
                if int(c.ID) <= prev {
5,457✔
320
                        panic("component arguments must be sorted by ID")
1✔
321
                }
322
                prev = int(c.ID)
5,455✔
323
                mask.Set(c.ID, true)
5,455✔
324

5,455✔
325
                size, align := c.Type.Size(), uintptr(c.Type.Align())
5,455✔
326
                size = (size + (align - 1)) / align * align
5,455✔
327

5,455✔
328
                a.buffers[i] = reflect.New(reflect.ArrayOf(cap, c.Type)).Elem()
5,455✔
329
                a.layouts[c.ID] = layout{
5,455✔
330
                        a.buffers[i].Addr().UnsafePointer(),
5,455✔
331
                        size,
5,455✔
332
                }
5,455✔
333
                a.indices.Set(c.ID, uint32(i))
5,455✔
334
        }
335
        a.entityBuffer = reflect.New(reflect.ArrayOf(cap, entityType)).Elem()
1,364✔
336

1,364✔
337
        a.archetypeAccess = archetypeAccess{
1,364✔
338
                basePointer:       unsafe.Pointer(&a.layouts[0]),
1,364✔
339
                entityPointer:     a.entityBuffer.Addr().UnsafePointer(),
1,364✔
340
                Mask:              mask,
1,364✔
341
                Relation:          relation,
1,364✔
342
                RelationComponent: node.relation,
1,364✔
343
        }
1,364✔
344

1,364✔
345
        a.graphNode = node
1,364✔
346

1,364✔
347
        a.len = 0
1,364✔
348
        a.cap = uint32(cap)
1,364✔
349
}
350

351
// Add adds an entity with optionally zeroed components to the archetype
352
func (a *archetype) Alloc(entity Entity) uintptr {
35,385✔
353
        idx := uintptr(a.len)
35,385✔
354
        a.extend(1)
35,385✔
355
        a.addEntity(idx, &entity)
35,385✔
356
        a.len++
35,385✔
357
        return idx
35,385✔
358
}
35,385✔
359

360
// AllocN allocates storage for the given number of entities.
361
func (a *archetype) AllocN(count uint32) {
27,578✔
362
        a.extend(count)
27,578✔
363
        a.len += count
27,578✔
364
}
27,578✔
365

366
// Add adds an entity with components to the archetype.
367
func (a *archetype) Add(entity Entity, components ...Component) uintptr {
9✔
368
        if len(components) != len(a.graphNode.Ids) {
10✔
369
                panic("Invalid number of components")
1✔
370
        }
371
        idx := uintptr(a.len)
8✔
372

8✔
373
        a.extend(1)
8✔
374
        a.addEntity(idx, &entity)
8✔
375
        for _, c := range components {
24✔
376
                lay := a.getLayout(c.ID)
16✔
377
                size := lay.itemSize
16✔
378
                if size == 0 {
18✔
379
                        continue
2✔
380
                }
381
                src := reflect.ValueOf(c.Comp).UnsafePointer()
14✔
382
                dst := a.Get(uintptr(idx), c.ID)
14✔
383
                a.copy(src, dst, size)
14✔
384
        }
385
        a.len++
8✔
386
        return idx
8✔
387
}
388

389
// Remove removes an entity and its components from the archetype.
390
//
391
// Performs a swap-remove and reports whether a swap was necessary
392
// (i.e. not the last entity that was removed).
393
func (a *archetype) Remove(index uintptr) bool {
30,552✔
394
        swapped := a.removeEntity(index)
30,552✔
395

30,552✔
396
        old := uintptr(a.len - 1)
30,552✔
397

30,552✔
398
        if index != old {
55,792✔
399
                for _, id := range a.graphNode.Ids {
50,628✔
400
                        lay := a.getLayout(id)
25,388✔
401
                        size := lay.itemSize
25,388✔
402
                        if size == 0 {
25,697✔
403
                                continue
309✔
404
                        }
405
                        src := unsafe.Add(lay.pointer, old*size)
25,079✔
406
                        dst := unsafe.Add(lay.pointer, index*size)
25,079✔
407
                        a.copy(src, dst, size)
25,079✔
408
                }
409
        }
410
        a.ZeroAll(old)
30,552✔
411
        a.len--
30,552✔
412

30,552✔
413
        return swapped
30,552✔
414
}
415

416
// ZeroAll resets a block of storage in all buffers.
417
func (a *archetype) ZeroAll(index uintptr) {
30,552✔
418
        for _, id := range a.graphNode.Ids {
59,257✔
419
                a.Zero(index, id)
28,705✔
420
        }
28,705✔
421
}
422

423
// ZeroAll resets a block of storage in one buffer.
424
func (a *archetype) Zero(index uintptr, id ID) {
28,705✔
425
        lay := a.getLayout(id)
28,705✔
426
        size := lay.itemSize
28,705✔
427
        if size == 0 {
31,847✔
428
                return
3,142✔
429
        }
3,142✔
430
        dst := unsafe.Add(lay.pointer, index*size)
25,563✔
431
        a.copy(a.graphNode.zeroPointer, dst, size)
25,563✔
432
}
433

434
// SetEntity overwrites an entity
435
func (a *archetype) SetEntity(index uintptr, entity Entity) {
2,756,293✔
436
        a.addEntity(index, &entity)
2,756,293✔
437
}
2,756,293✔
438

439
// Set overwrites a component with the data behind the given pointer
440
func (a *archetype) Set(index uintptr, id ID, comp interface{}) unsafe.Pointer {
835✔
441
        lay := a.getLayout(id)
835✔
442
        dst := a.Get(index, id)
835✔
443
        size := lay.itemSize
835✔
444
        if size == 0 {
865✔
445
                return dst
30✔
446
        }
30✔
447
        rValue := reflect.ValueOf(comp)
805✔
448

805✔
449
        src := rValue.UnsafePointer()
805✔
450
        a.copy(src, dst, size)
805✔
451
        return dst
805✔
452
}
453

454
// SetPointer overwrites a component with the data behind the given pointer
455
func (a *archetype) SetPointer(index uintptr, id ID, comp unsafe.Pointer) unsafe.Pointer {
5,859✔
456
        lay := a.getLayout(id)
5,859✔
457
        dst := a.Get(index, id)
5,859✔
458
        size := lay.itemSize
5,859✔
459
        if size == 0 {
10,293✔
460
                return dst
4,434✔
461
        }
4,434✔
462

463
        a.copy(comp, dst, size)
1,425✔
464
        return dst
1,425✔
465
}
466

467
// Reset removes all entities and components.
468
//
469
// Does NOT free the reserved memory.
470
func (a *archetype) Reset() {
52,503✔
471
        if a.len == 0 {
77,548✔
472
                return
25,045✔
473
        }
25,045✔
474
        a.len = 0
27,458✔
475
        for _, buf := range a.buffers {
54,929✔
476
                buf.SetZero()
27,471✔
477
        }
27,471✔
478
}
479

480
// Deactivate the archetype for later re-use.
481
func (a *archetype) Deactivate() {
27,413✔
482
        a.Reset()
27,413✔
483
        a.index = -1
27,413✔
484
}
27,413✔
485

486
// Activate reactivates a de-activated archetype.
487
func (a *archetype) Activate(target Entity, index int32) {
27,402✔
488
        a.index = index
27,402✔
489
        a.Relation = target
27,402✔
490
}
27,402✔
491

492
// IsActive returns whether the archetype is active.
493
// Otherwise, it is eligible for re-use.
494
func (a *archetype) IsActive() bool {
7,603,092✔
495
        return a.index >= 0
7,603,092✔
496
}
7,603,092✔
497

498
// Components returns the component IDs for this archetype
499
func (a *archetype) Components() []ID {
2,953✔
500
        return a.graphNode.Ids
2,953✔
501
}
2,953✔
502

503
// Len reports the number of entities in the archetype
504
func (a *archetype) Len() uint32 {
5,261,501✔
505
        return a.len
5,261,501✔
506
}
5,261,501✔
507

508
// Cap reports the current capacity of the archetype
509
func (a *archetype) Cap() uint32 {
5,100,047✔
510
        return a.cap
5,100,047✔
511
}
5,100,047✔
512

513
// Stats generates statistics for an archetype
514
func (a *archetype) Stats(reg *componentRegistry[ID]) stats.ArchetypeStats {
113✔
515
        ids := a.Components()
113✔
516
        aCompCount := len(ids)
113✔
517
        aTypes := make([]reflect.Type, aCompCount)
113✔
518
        for j, id := range ids {
225✔
519
                aTypes[j], _ = reg.ComponentType(id)
112✔
520
        }
112✔
521

522
        cap := int(a.Cap())
113✔
523
        memPerEntity := 0
113✔
524
        for _, id := range a.graphNode.Ids {
225✔
525
                lay := a.getLayout(id)
112✔
526
                memPerEntity += int(lay.itemSize)
112✔
527
        }
112✔
528
        memory := cap * (int(entitySize) + memPerEntity)
113✔
529

113✔
530
        return stats.ArchetypeStats{
113✔
531
                IsActive: a.IsActive(),
113✔
532
                Size:     int(a.Len()),
113✔
533
                Capacity: cap,
113✔
534
                Memory:   memory,
113✔
535
        }
113✔
536
}
537

538
// UpdateStats updates statistics for an archetype
539
func (a *archetype) UpdateStats(node *stats.NodeStats, stats *stats.ArchetypeStats, reg *componentRegistry[ID]) {
5,099,924✔
540
        cap := int(a.Cap())
5,099,924✔
541
        memory := cap * (int(entitySize) + node.MemoryPerEntity)
5,099,924✔
542

5,099,924✔
543
        stats.IsActive = a.IsActive()
5,099,924✔
544
        stats.Size = int(a.Len())
5,099,924✔
545
        stats.Capacity = cap
5,099,924✔
546
        stats.Memory = memory
5,099,924✔
547
}
5,099,924✔
548

549
// copy from one pointer to another.
550
func (a *archetype) copy(src, dst unsafe.Pointer, itemSize uintptr) {
2,869,812✔
551
        dstSlice := (*[math.MaxInt32]byte)(dst)[:itemSize:itemSize]
2,869,812✔
552
        srcSlice := (*[math.MaxInt32]byte)(src)[:itemSize:itemSize]
2,869,812✔
553
        copy(dstSlice, srcSlice)
2,869,812✔
554
}
2,869,812✔
555

556
// extend the memory buffers if necessary for adding an entity.
557
func (a *archetype) extend(by uint32) {
62,974✔
558
        required := a.len + by
62,974✔
559
        if a.cap >= required {
125,903✔
560
                return
62,929✔
561
        }
62,929✔
562
        a.cap = capacityU32(required, a.graphNode.capacityIncrement)
45✔
563

45✔
564
        old := a.entityBuffer
45✔
565
        a.entityBuffer = reflect.New(reflect.ArrayOf(int(a.cap), entityType)).Elem()
45✔
566
        a.entityPointer = a.entityBuffer.Addr().UnsafePointer()
45✔
567
        reflect.Copy(a.entityBuffer, old)
45✔
568

45✔
569
        for _, id := range a.graphNode.Ids {
95✔
570
                lay := a.getLayout(id)
50✔
571
                if lay.itemSize == 0 {
56✔
572
                        continue
6✔
573
                }
574
                index, _ := a.indices.Get(id)
44✔
575
                old := a.buffers[index]
44✔
576
                a.buffers[index] = reflect.New(reflect.ArrayOf(int(a.cap), old.Type().Elem())).Elem()
44✔
577
                lay.pointer = a.buffers[index].Addr().UnsafePointer()
44✔
578
                reflect.Copy(a.buffers[index], old)
44✔
579
        }
580
}
581

582
// Adds an entity at the given index. Does not extend the entity buffer.
583
func (a *archetype) addEntity(index uintptr, entity *Entity) {
2,791,686✔
584
        dst := unsafe.Add(a.entityPointer, entitySize*index)
2,791,686✔
585
        src := unsafe.Pointer(entity)
2,791,686✔
586
        a.copy(src, dst, entitySize)
2,791,686✔
587
}
2,791,686✔
588

589
// removeEntity removes an entity from tne archetype.
590
// Components need to be removed separately.
591
func (a *archetype) removeEntity(index uintptr) bool {
30,552✔
592
        old := uintptr(a.len - 1)
30,552✔
593

30,552✔
594
        if index == old {
35,864✔
595
                return false
5,312✔
596
        }
5,312✔
597

598
        src := unsafe.Add(a.entityPointer, old*entitySize)
25,240✔
599
        dst := unsafe.Add(a.entityPointer, index*entitySize)
25,240✔
600
        a.copy(src, dst, entitySize)
25,240✔
601

25,240✔
602
        return true
25,240✔
603
}
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