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

mlange-42 / ark / 13639292298

03 Mar 2025 07:56PM CUT coverage: 98.495%. Remained the same
13639292298

push

github

web-flow
Optimize matching of tables against relations (#115)

1 of 1 new or added line in 1 file covered. (100.0%)

5694 of 5781 relevant lines covered (98.5%)

34789.79 hits per line

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

94.69
/ecs/pool.go
1
package ecs
2

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

9
type number interface {
10
        int | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64 | cacheID
11
}
12

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

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

39
// Get returns a fresh or recycled entity.
40
func (p *entityPool) Get() Entity {
507,350✔
41
        if p.available == 0 {
514,795✔
42
                return p.getNew()
7,445✔
43
        }
7,445✔
44
        curr := p.next
499,905✔
45
        p.next, p.entities[p.next].id = p.entities[p.next].id, p.next
499,905✔
46
        p.available--
499,905✔
47
        return p.entities[curr]
499,905✔
48
}
49

50
// Allocates and returns a new entity. For internal use.
51
func (p *entityPool) getNew() Entity {
7,445✔
52
        e := Entity{id: entityID(len(p.entities)), gen: 0}
7,445✔
53
        p.entities = append(p.entities, e)
7,445✔
54
        p.pointer = unsafe.Pointer(&p.entities[0])
7,445✔
55
        return e
7,445✔
56
}
7,445✔
57

58
// Recycle hands an entity back for recycling.
59
func (p *entityPool) Recycle(e Entity) {
502,658✔
60
        if e.id < p.reserved {
502,660✔
61
                panic("can't recycle reserved zero or wildcard entity")
2✔
62
        }
63
        p.entities[e.id].gen++
502,656✔
64
        p.next, p.entities[e.id].id = e.id, p.next
502,656✔
65
        p.available++
502,656✔
66
}
67

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

75
// Alive returns whether an entity is still alive, based on the entity's generations.
76
func (p *entityPool) Alive(e Entity) bool {
1,012,550✔
77
        return e.gen == (*Entity)(unsafe.Add(p.pointer, entitySize*uintptr(e.id))).gen
1,012,550✔
78
}
1,012,550✔
79

80
// Len returns the current number of used entities.
81
func (p *entityPool) Len() int {
33✔
82
        return len(p.entities) - int(p.reserved) - int(p.available)
33✔
83
}
33✔
84

85
// Cap returns the current capacity (used and recycled entities).
86
func (p *entityPool) Cap() int {
×
87
        return len(p.entities) - int(p.reserved)
×
88
}
×
89

90
// TotalCap returns the current capacity in terms of reserved memory.
91
func (p *entityPool) TotalCap() int {
×
92
        return cap(p.entities)
×
93
}
×
94

95
// Available returns the current number of available/recycled entities.
96
func (p *entityPool) Available() int {
32✔
97
        return int(p.available)
32✔
98
}
32✔
99

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

110
// Get returns a fresh or recycled bit.
111
func (p *bitPool) Get() uint8 {
1,964✔
112
        if p.available == 0 {
2,619✔
113
                return p.getNew()
655✔
114
        }
655✔
115
        curr := p.next
1,309✔
116
        p.next, p.bits[p.next] = p.bits[p.next], p.next
1,309✔
117
        p.available--
1,309✔
118
        return p.bits[curr]
1,309✔
119
}
120

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

134
// Recycle hands a bit back for recycling.
135
func (p *bitPool) Recycle(b uint8) {
1,424✔
136
        p.next, p.bits[b] = b, p.next
1,424✔
137
        p.available++
1,424✔
138
}
1,424✔
139

140
// Reset recycles all bits.
141
func (p *bitPool) Reset() {
2✔
142
        p.next = 0
2✔
143
        p.length = 0
2✔
144
        p.available = 0
2✔
145
}
2✔
146

147
// entityPool is an implementation using implicit linked lists.
148
// Implements https://skypjack.github.io/2019-05-06-ecs-baf-part-3/
149
type intPool[T number] struct {
150
        next              T
151
        pool              []T
152
        available         uint32
153
        capacityIncrement uint32
154
}
155

156
// newEntityPool creates a new, initialized Entity pool.
157
func newIntPool[T number](capacityIncrement uint32) intPool[T] {
190✔
158
        return intPool[T]{
190✔
159
                pool:              make([]T, 0, capacityIncrement),
190✔
160
                next:              0,
190✔
161
                available:         0,
190✔
162
                capacityIncrement: capacityIncrement,
190✔
163
        }
190✔
164
}
190✔
165

166
// Get returns a fresh or recycled entity.
167
func (p *intPool[T]) Get() T {
132✔
168
        if p.available == 0 {
258✔
169
                return p.getNew()
126✔
170
        }
126✔
171
        curr := p.next
6✔
172
        p.next, p.pool[p.next] = p.pool[p.next], p.next
6✔
173
        p.available--
6✔
174
        return p.pool[curr]
6✔
175
}
176

177
// Allocates and returns a new entity. For internal use.
178
func (p *intPool[T]) getNew() T {
126✔
179
        e := T(len(p.pool))
126✔
180
        if len(p.pool) == cap(p.pool) {
127✔
181
                old := p.pool
1✔
182
                p.pool = make([]T, len(p.pool), len(p.pool)+int(p.capacityIncrement))
1✔
183
                copy(p.pool, old)
1✔
184
        }
1✔
185
        p.pool = append(p.pool, e)
126✔
186
        return e
126✔
187
}
188

189
// Recycle hands an entity back for recycling.
190
func (p *intPool[T]) Recycle(e T) {
6✔
191
        p.next, p.pool[e] = e, p.next
6✔
192
        p.available++
6✔
193
}
6✔
194

195
// Reset recycles all entities. Does NOT free the reserved memory.
196
func (p *intPool[T]) Reset() {
3✔
197
        p.pool = p.pool[:0]
3✔
198
        p.next = 0
3✔
199
        p.available = 0
3✔
200
}
3✔
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