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

mlange-42 / ark / 13610747635

02 Mar 2025 01:27AM CUT coverage: 97.424%. Remained the same
13610747635

push

github

web-flow
Group and rename pos/vel benchmarks (#99)

3895 of 3998 relevant lines covered (97.42%)

45948.49 hits per line

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

92.41
/ecs/pool.go
1
package ecs
2

3
import (
4
        "fmt"
5
        "math"
6
        "unsafe"
7
)
8

9
// entityPool is an implementation using implicit linked lists.
10
// Implements https://skypjack.github.io/2019-05-06-ecs-baf-part-3/
11
type entityPool struct {
12
        entities  []Entity
13
        next      entityID
14
        available uint32
15
        pointer   unsafe.Pointer
16
        reserved  entityID
17
}
18

19
// newEntityPool creates a new, initialized Entity pool.
20
func newEntityPool(initialCapacity uint32, reserved uint32) entityPool {
124✔
21
        entities := make([]Entity, reserved, initialCapacity+reserved)
124✔
22
        // Reserved zero and wildcard entities.
124✔
23
        for i := range reserved {
372✔
24
                entities[i] = Entity{entityID(i), math.MaxUint32}
248✔
25
        }
248✔
26
        return entityPool{
124✔
27
                entities:  entities,
124✔
28
                next:      0,
124✔
29
                available: 0,
124✔
30
                pointer:   unsafe.Pointer(&entities[0]),
124✔
31
                reserved:  entityID(reserved),
124✔
32
        }
124✔
33
}
34

35
// Get returns a fresh or recycled entity.
36
func (p *entityPool) Get() Entity {
495,901✔
37
        if p.available == 0 {
501,942✔
38
                return p.getNew()
6,041✔
39
        }
6,041✔
40
        curr := p.next
489,860✔
41
        p.next, p.entities[p.next].id = p.entities[p.next].id, p.next
489,860✔
42
        p.available--
489,860✔
43
        return p.entities[curr]
489,860✔
44
}
45

46
// Allocates and returns a new entity. For internal use.
47
func (p *entityPool) getNew() Entity {
6,041✔
48
        e := Entity{id: entityID(len(p.entities)), gen: 0}
6,041✔
49
        p.entities = append(p.entities, e)
6,041✔
50
        p.pointer = unsafe.Pointer(&p.entities[0])
6,041✔
51
        return e
6,041✔
52
}
6,041✔
53

54
// Recycle hands an entity back for recycling.
55
func (p *entityPool) Recycle(e Entity) {
493,556✔
56
        if e.id < p.reserved {
493,558✔
57
                panic("can't recycle reserved zero or wildcard entity")
2✔
58
        }
59
        p.entities[e.id].gen++
493,554✔
60
        p.next, p.entities[e.id].id = e.id, p.next
493,554✔
61
        p.available++
493,554✔
62
}
63

64
// Reset recycles all entities. Does NOT free the reserved memory.
65
func (p *entityPool) Reset() {
33✔
66
        p.entities = p.entities[:p.reserved]
33✔
67
        p.next = 0
33✔
68
        p.available = 0
33✔
69
}
33✔
70

71
// Alive returns whether an entity is still alive, based on the entity's generations.
72
func (p *entityPool) Alive(e Entity) bool {
992,578✔
73
        return e.gen == (*Entity)(unsafe.Add(p.pointer, entitySize*uintptr(e.id))).gen
992,578✔
74
}
992,578✔
75

76
// Len returns the current number of used entities.
77
func (p *entityPool) Len() int {
33✔
78
        return len(p.entities) - int(p.reserved) - int(p.available)
33✔
79
}
33✔
80

81
// Cap returns the current capacity (used and recycled entities).
82
func (p *entityPool) Cap() int {
×
83
        return len(p.entities) - int(p.reserved)
×
84
}
×
85

86
// TotalCap returns the current capacity in terms of reserved memory.
87
func (p *entityPool) TotalCap() int {
×
88
        return cap(p.entities)
×
89
}
×
90

91
// Available returns the current number of available/recycled entities.
92
func (p *entityPool) Available() int {
32✔
93
        return int(p.available)
32✔
94
}
32✔
95

96
// bitPool is a pool of bits that makes it possible to obtain an un-set bit,
97
// and to recycle that bit for later use.
98
// This implementation uses an implicit list.
99
type bitPool struct {
100
        length    uint16
101
        bits      [MaskTotalBits]uint8
102
        next      uint8
103
        available uint8
104
}
105

106
// Get returns a fresh or recycled bit.
107
func (p *bitPool) Get() uint8 {
1,642✔
108
        if p.available == 0 {
2,215✔
109
                return p.getNew()
573✔
110
        }
573✔
111
        curr := p.next
1,069✔
112
        p.next, p.bits[p.next] = p.bits[p.next], p.next
1,069✔
113
        p.available--
1,069✔
114
        return p.bits[curr]
1,069✔
115
}
116

117
// Allocates and returns a new bit. For internal use.
118
func (p *bitPool) getNew() uint8 {
573✔
119
        if p.length >= MaskTotalBits {
576✔
120
                panic(fmt.Sprintf("run out of the maximum of %d bits. "+
3✔
121
                        "This is likely caused by unclosed queries that lock the world. "+
3✔
122
                        "Make sure that all queries finish their iteration or are closed manually", MaskTotalBits))
3✔
123
        }
124
        b := uint8(p.length)
570✔
125
        p.bits[p.length] = b
570✔
126
        p.length++
570✔
127
        return b
570✔
128
}
129

130
// Recycle hands a bit back for recycling.
131
func (p *bitPool) Recycle(b uint8) {
1,127✔
132
        p.next, p.bits[b] = b, p.next
1,127✔
133
        p.available++
1,127✔
134
}
1,127✔
135

136
// Reset recycles all bits.
137
func (p *bitPool) Reset() {
2✔
138
        p.next = 0
2✔
139
        p.length = 0
2✔
140
        p.available = 0
2✔
141
}
2✔
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