a flat bitset

This commit is contained in:
Tnze
2022-05-28 01:39:33 +08:00
parent 7bb5c2db87
commit 29ce03be31
3 changed files with 120 additions and 58 deletions

View File

@ -1,58 +0,0 @@
package ecs
type BitSetLike interface {
Set(i Index) (old bool)
Unset(i Index) (old bool)
Contains(i Index) bool
And(other BitSetLike) (result BitSetLike)
AndNot(other BitSetLike) (result BitSetLike)
Range(f func(eid Index))
}
type BitSet struct {
// TODO: this is not a BitSet, I'm just testing
values map[Index]struct{}
}
func (b BitSet) Set(i Index) (old bool) {
_, old = b.values[i]
b.values[i] = struct{}{}
return
}
func (b BitSet) Unset(i Index) (old bool) {
_, old = b.values[i]
delete(b.values, i)
return
}
func (b BitSet) Contains(i Index) bool {
_, contains := b.values[i]
return contains
}
func (b BitSet) And(other BitSetLike) BitSetLike {
result := BitSet{values: make(map[Index]struct{})}
for i := range b.values {
if other.Contains(i) {
result.values[i] = struct{}{}
}
}
return result
}
func (b BitSet) AndNot(other BitSetLike) BitSetLike {
result := BitSet{values: make(map[Index]struct{})}
for i := range b.values {
if !other.Contains(i) {
result.values[i] = struct{}{}
}
}
return result
}
func (b BitSet) Range(f func(eid Index)) {
for i := range b.values {
f(i)
}
}

92
server/ecs/bitset.go Normal file
View File

@ -0,0 +1,92 @@
package ecs
import (
"golang.org/x/exp/constraints"
"math/bits"
"unsafe"
)
const uintsize = uint(unsafe.Sizeof(BitSet{}.values[0]))
type BitSet struct {
// TODO: this is not a BitSet, I'm just testing
values []uint
}
func (b *BitSet) Set(i Index) (old bool) {
index := uint(i) / uintsize
offset := uint(i) % uintsize
if index >= uint(len(b.values)) {
if index < uint(cap(b.values)) {
b.values = b.values[:index]
} else {
newValues := make([]uint, index+1)
copy(newValues, b.values)
b.values = newValues
}
}
v := &b.values[index]
mask := uint(1 << offset)
old = *v&mask != 0
*v |= mask
return
}
func (b *BitSet) Unset(i Index) (old bool) {
index := uint(i) / uintsize
offset := uint(i) % uintsize
if index < uint(len(b.values)) {
v := &b.values[index]
mask := uint(1 << offset)
old = *v&mask != 0
*v &= ^mask
}
return
}
func (b *BitSet) Contains(i Index) bool {
index := uint(i) / uintsize
offset := uint(i) % uintsize
return index < uint(len(b.values)) && b.values[index]&(1<<offset) != 0
}
func (b *BitSet) And(other *BitSet) *BitSet {
result := BitSet{values: make([]uint, min(len(b.values), len(other.values)))}
for i := range b.values {
result.values[i] = b.values[i] & other.values[i]
}
return &result
}
func (b *BitSet) AndNot(other BitSet) *BitSet {
result := BitSet{values: make([]uint, max(len(b.values), len(other.values)))}
for i := range b.values {
result.values[i] = b.values[i] & ^other.values[i]
}
return &result
}
func (b *BitSet) Range(f func(eid Index)) {
for i, v := range b.values {
base := int(unsafe.Sizeof(v)) * i
for v != 0 {
p := bits.TrailingZeros(v)
f(Index(base + p))
v ^= 1 << p
}
}
}
func max[T constraints.Integer](a, b T) T {
if a > b {
return a
}
return b
}
func min[T constraints.Integer](a, b T) T {
if a < b {
return a
}
return b
}

28
server/ecs/bitset_test.go Normal file
View File

@ -0,0 +1,28 @@
package ecs
import (
"reflect"
"testing"
)
func TestBitSet_And(t *testing.T) {
var set1, set2 BitSet
set1.Set(1)
set1.Set(3)
set1.Set(40)
set2.Set(2)
set2.Set(3)
set2.Set(9)
set2.Set(40)
var results []Index
set1.And(&set2).Range(func(eid Index) {
results = append(results, eid)
})
want := []Index{3, 40}
if !reflect.DeepEqual(results, want) {
t.Errorf("want %v, got: %v", want, results)
}
}