diff --git a/pkg/util/bitfield/bitfield.go b/pkg/util/bitfield/bitfield.go new file mode 100644 index 000000000..26344c4c8 --- /dev/null +++ b/pkg/util/bitfield/bitfield.go @@ -0,0 +1,64 @@ +/* +Package bitfield provides a simple and efficient arbitrary size bit field implementation. +It doesn't attempt to cover everything that could be done with bit fields, +providing only things used by neo-go. +*/ +package bitfield + +// Field is a bit field represented as a slice of uint64 values. +type Field []uint64 + +// Bits and bytes count in a basic element of Field. +const elemBits = 64 +const elemBytes = 8 + +// New creates a new bit field of specified length. Actual field length +// can be rounded to the next multiple of 64, so it's a responsibility +// of the user to deal with that. +func New(n int) Field { + return make(Field, 1+(n-1)/elemBits) +} + +// Set sets one bit at specified offset. No bounds checking is done. +func (f Field) Set(i int) { + addr, offset := (i / elemBits), (i % elemBits) + f[addr] |= (1 << offset) +} + +// IsSet returns true if the bit with specified offset is set. +func (f Field) IsSet(i int) bool { + addr, offset := (i / elemBits), (i % elemBits) + return (f[addr] & (1 << offset)) != 0 +} + +// Copy makes a copy of current Field. +func (f Field) Copy() Field { + fn := make(Field, len(f)) + copy(fn, f) + return fn +} + +// And implements logical AND between f's and m's bits saving the result into f. +func (f Field) And(m Field) { + l := len(m) + for i := range f { + if i >= l { + f[i] = 0 + continue + } + f[i] &= m[i] + } +} + +// Equals compares two Fields and returns true if they're equal. +func (f Field) Equals(o Field) bool { + if len(f) != len(o) { + return false + } + for i := range f { + if f[i] != o[i] { + return false + } + } + return true +} diff --git a/pkg/util/bitfield/bitfield_test.go b/pkg/util/bitfield/bitfield_test.go new file mode 100644 index 000000000..38c4ab1af --- /dev/null +++ b/pkg/util/bitfield/bitfield_test.go @@ -0,0 +1,42 @@ +package bitfield + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFields(t *testing.T) { + a := New(128) + b := New(128) + a.Set(10) + b.Set(10) + a.Set(42) + b.Set(42) + a.Set(100) + b.Set(100) + require.True(t, a.IsSet(42)) + require.False(t, b.IsSet(43)) + + v := uint64(1<<10 | 1<<42) + require.Equal(t, v, a[0]) + require.Equal(t, v, b[0]) + + require.True(t, a.Equals(b)) + + c := a.Copy() + require.True(t, c.Equals(b)) + + z := New(128) + c.And(a) + require.True(t, c.Equals(b)) + c.And(z) + require.True(t, c.Equals(z)) + + c = New(64) + c[0] = a[0] + require.False(t, c.Equals(a)) + + b.And(c) + require.False(t, b.Equals(a)) +}