Bitfield
This library is for working with enum of binary representation.
The most often case that we're working this is RBAC but usable
in any other cases that's needed to set some rules with bits.
With this we can abstract out the numerical enum and some
bits trickery while still using the same underlying
efficiency and performance.
Our current bitfield is each limited to 32 fields that should be
usable in most cases.
How to use
In order to be convertible between value and sets, we need to
have our enum implements the interface Enumerator
which simply
returning the possible values of our enums.
For example we have enum as below:
type myenum int
const (
field1 myenum = iota
field2
field3
field4
)
func (myenum) Enums() []myenum {
return []myenum{field1, field2, field3, field4}
}
Note: we can also to have myenum
to implement fmt.Stringer
With above defined, we can use it as
bits := bitfield.New[myenum]() // in this case we set all fields 0
if bits.Value() != 0 { panic(bits.Value()) }
bits = bitfield.New[myenum](field1, field4)
if bits.Value() != 9 { panic(bits.Value()) }
if bits.Has(field2) { panic("expected to don't have field2") }
if !bits.Has(field1) { panic("expected to have field1") }
Our bitfield
package is simple and only has several exported API viz:
New[Enumerator[E]](e ...E) Bitfield[E]
to initiate Bitfield
object.
Bitfield.Has[E](e ...E) bool
to test whether e
is/are set in Bitfield
object.
Bitfield.Include[E](e ...E)
to set our bit field.
Bitfield.Exclude[E](e ...E)
to clear out bit field.
Bitfield.Value() uint32
to get its integer representation.
From[Enumerator[E]](uint) Bitfield[E]
to convert our value to Bitfield
object.
Install
Simply
go get github.com/mashingan/bitfield
or
within file just import it and run go mod tidy
will automatically add
this lib to your project.
Example
Let's create bits roles in our RBAC.
1. Bit 1: Identify whether the user is superadmin
2. Bit 2: Identify whether the user is admin
3. Bit 3: Identify whether the user is user
4. Bit 4: Read permission
5. Bit 5: Write permission
6. Bit 6: Delete permission
Using above roles, we can use Bitfield
as below
package main
import (
"log"
"github.com/mashingan/bitfield"
)
/*
1. Bit 1: Identify whether the user is superadmin
2. Bit 2: Identify whether the user is admin
3. Bit 3: Identify whether the user is user
4. Bit 4: Read permission
5. Bit 5: Write permission
6. Bit 6: Delete permission
*/
type RoleAcc byte
// our enum bitfield bits definition
const (
raSuperAdmin RoleAcc = iota
raAdmin
raUser
raRead
raWrite
raDelete
)
func (RoleAcc) Enums() []RoleAcc {
return []RoleAcc{
raSuperAdmin, raAdmin, raUser,
raRead, raWrite, raDelete,
}
}
var rolestr = map[RoleAcc]string{
raSuperAdmin: "super_admin",
raAdmin: "admin",
raUser: "user",
raRead: "read",
raWrite: "write",
raDelete: "delete",
}
func (r RoleAcc) String() string {
return rolestr[r]
}
func main() {
// let's create a super admin only
user := bitfield.New[RoleAcc](raSuperAdmin)
if user.Has(raAdmin) {
log.Fatal("expected user only has role super admin, got admin")
}
if len(user.Sets()) != 1 {
log.Fatal("expected user only has role sets: [super_admin], got:", user.Sets())
}
if user.Value() != 1 {
log.Fatal("expected user role value is 1, got:", user.Value())
}
user.Exclude(raSuperAdmin)
if user.Has(raSuperAdmin) {
log.Fatal("expected user doesn't have any role, got superadmin")
}
if len(user.Sets()) != 0 {
log.Fatal("expected user has empty role sets, got:", user.Sets())
}
if user.Value() != 0 {
log.Fatal("expected user role value is 0, got:", user.Value())
}
user = bitfield.From[RoleAcc](58) // re-create from value
log.Println("user sets:", user.Sets())
if !user.Has(raAdmin) {
log.Fatal("expected user is admin")
}
if !user.Has(raRead) || !user.Has(raWrite) || !user.Has(raDelete) {
log.Fatal("expected user able to read, write, and delete, got:", user.Sets())
}
}
License
MIT