From 29ce03be31ef216e3a93067acd31b3b40c79afce Mon Sep 17 00:00:00 2001 From: Tnze Date: Sat, 28 May 2022 01:39:33 +0800 Subject: [PATCH] a flat bitset --- server/ecs/bitest.go | 58 ------------------------ server/ecs/bitset.go | 92 +++++++++++++++++++++++++++++++++++++++ server/ecs/bitset_test.go | 28 ++++++++++++ 3 files changed, 120 insertions(+), 58 deletions(-) delete mode 100644 server/ecs/bitest.go create mode 100644 server/ecs/bitset.go create mode 100644 server/ecs/bitset_test.go diff --git a/server/ecs/bitest.go b/server/ecs/bitest.go deleted file mode 100644 index c33b9f5..0000000 --- a/server/ecs/bitest.go +++ /dev/null @@ -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) - } -} diff --git a/server/ecs/bitset.go b/server/ecs/bitset.go new file mode 100644 index 0000000..28ab362 --- /dev/null +++ b/server/ecs/bitset.go @@ -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< b { + return a + } + return b +} + +func min[T constraints.Integer](a, b T) T { + if a < b { + return a + } + return b +} diff --git a/server/ecs/bitset_test.go b/server/ecs/bitset_test.go new file mode 100644 index 0000000..ad82bbf --- /dev/null +++ b/server/ecs/bitset_test.go @@ -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) + } +}