Foundation for the aego engine. core/ errs, log, handle arena, mathx, clock, stats platform SDL3 backend plus a headless one, input state, events gpu device abstraction with gl33 and null backends vfs scheme-based virtual filesystem with overlay mounts engine builder, module registry, phase-ordered system loop, jobs demos basic, resize, input, headless; sandbox driver OpenGL bindings are generated into gpu/gl33/gl by tools/gen-gl.sh, so the module has no external dependencies.
115 lines
2.1 KiB
Go
115 lines
2.1 KiB
Go
package handle
|
|
|
|
type Handle[T any] struct {
|
|
Index uint32
|
|
Gen uint32
|
|
}
|
|
|
|
func (h Handle[T]) IsZero() bool {
|
|
return h.Gen == 0
|
|
}
|
|
|
|
func Cast[U, T any](h Handle[T]) Handle[U] {
|
|
return Handle[U](h)
|
|
}
|
|
|
|
type Arena[T any] struct {
|
|
items []T
|
|
gens []uint32
|
|
alive []bool
|
|
free []uint32
|
|
count int
|
|
OnGrow func(oldCap, newCap int)
|
|
}
|
|
|
|
func NewArena[T any](capacity int) *Arena[T] {
|
|
if capacity < 1 {
|
|
capacity = 1
|
|
}
|
|
return &Arena[T]{
|
|
items: make([]T, 0, capacity),
|
|
gens: make([]uint32, 0, capacity),
|
|
alive: make([]bool, 0, capacity),
|
|
free: make([]uint32, 0, capacity),
|
|
}
|
|
}
|
|
|
|
func (a *Arena[T]) Alloc() (Handle[T], *T) {
|
|
var idx uint32
|
|
if n := len(a.free); n > 0 {
|
|
idx = a.free[n-1]
|
|
a.free = a.free[:n-1]
|
|
} else {
|
|
if len(a.items) == cap(a.items) && a.OnGrow != nil {
|
|
a.OnGrow(cap(a.items), cap(a.items)*2)
|
|
}
|
|
var zero T
|
|
idx = uint32(len(a.items))
|
|
a.items = append(a.items, zero)
|
|
a.gens = append(a.gens, 0)
|
|
a.alive = append(a.alive, false)
|
|
}
|
|
gen := a.gens[idx] + 1
|
|
if gen == 0 {
|
|
gen = 1
|
|
}
|
|
a.gens[idx] = gen
|
|
a.alive[idx] = true
|
|
a.count++
|
|
return Handle[T]{Index: idx, Gen: gen}, &a.items[idx]
|
|
}
|
|
|
|
func (a *Arena[T]) Valid(h Handle[T]) bool {
|
|
return h.Gen != 0 &&
|
|
int(h.Index) < len(a.items) &&
|
|
a.alive[h.Index] &&
|
|
a.gens[h.Index] == h.Gen
|
|
}
|
|
|
|
func (a *Arena[T]) Get(h Handle[T]) (*T, bool) {
|
|
if !a.Valid(h) {
|
|
return nil, false
|
|
}
|
|
return &a.items[h.Index], true
|
|
}
|
|
|
|
func (a *Arena[T]) Free(h Handle[T]) bool {
|
|
if !a.Valid(h) {
|
|
return false
|
|
}
|
|
var zero T
|
|
a.items[h.Index] = zero
|
|
a.alive[h.Index] = false
|
|
a.free = append(a.free, h.Index)
|
|
a.count--
|
|
return true
|
|
}
|
|
|
|
func (a *Arena[T]) Len() int {
|
|
return a.count
|
|
}
|
|
|
|
func (a *Arena[T]) Cap() int {
|
|
return cap(a.items)
|
|
}
|
|
|
|
func (a *Arena[T]) Each(fn func(Handle[T], *T)) {
|
|
for i := range a.items {
|
|
if a.alive[i] {
|
|
fn(Handle[T]{Index: uint32(i), Gen: a.gens[i]}, &a.items[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *Arena[T]) Clear() {
|
|
var zero T
|
|
a.free = a.free[:0]
|
|
n := len(a.items)
|
|
for i := range a.items {
|
|
a.items[i] = zero
|
|
a.alive[i] = false
|
|
a.free = append(a.free, uint32(n-1-i))
|
|
}
|
|
a.count = 0
|
|
}
|