bitset

package module
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2025 License: MIT Imports: 2 Imported by: 1

README

bitset

Build Status Go Report Card GoDoc Coverage Status

bitset is a lightweight Go package that provides a typical bit set data structure using a []byte slice. It allows fast and compact storage of bits, with utility methods for checking, setting, cloning, parsing, and serializing bitsets. Bits are stored from the most significant bit (MSB) of each byte.

Motivation

The main motivation behind this package was to provide an efficient and compact representation of user permissions within JWT tokens, avoiding repeated calls to an external authorization service.

In a typical authorization system, each permission (like read_user, edit_order, etc.) is assigned a unique identifier that corresponds to a bit position in a bitmask.

Let’s assume that perms and roles are stored in the database. All available permissions are stored in a permissions table:

CREATE TABLE permissions (
    id      TEXT    PRIMARY KEY,
    title   TEXT    NOT NULL,
    bit_pos INTEGER NOT NULL
);

INSERT INTO permissions (id, title, bit_pos) VALUES
('read_user',       'Read user data',            0),
('edit_user',       'Edit user profile',         1),
('delete_user',     'Delete user account',       2),
('create_order',    'Create an order',           3),
('edit_order',      'Modify existing order',     4),
('cancel_order',    'Cancel order',              5),
('manage_payments', 'Access payment settings',   6),
('admin_panel',     'Access admin panel',        7);

User roles (e.g., user, admin) are defined in a roles table, each containing an array of permission IDs:

CREATE TABLE roles (
    id              INT4    PRIMARY KEY,
    name            TEXT    NOT NULL,
    permission_ids  TEXT[]  NOT NULL
);

INSERT INTO roles (id, name, permission_ids) VALUES
(1, 'Regular User',        ARRAY['read_user', 'create_order']),
(2, 'Moderator',           ARRAY['read_user', 'edit_user', 'edit_order']),
(3, 'Administrator',       ARRAY['read_user', 'edit_user', 'delete_user', 'create_order', 'edit_order', 'cancel_order', 'manage_payments']),
(4, 'Super Admin',         ARRAY['read_user', 'edit_user', 'delete_user', 'create_order', 'edit_order', 'cancel_order', 'manage_payments', 'admin_panel']);

When a JWT token is generated, the list of permissions associated with a role is converted into a bitmask. This bitmask is then serialized as a hexadecimal string and embedded into the JWT payload.

Example:
Permissions: read_user, create_order → Bit positions: 0 and 3 → Bitmask: 10010000 → Hex string: "90"

Why this is useful
  • Compact token size — bitmask in hex is far smaller than an array of strings
  • Instant permission checks — no need to call external services
  • Unified permission model — easily shared across distributed services
  • Minimal HTTP overhead — especially in the Authorization header

This package provides robust tools for encoding, decoding, and validating such bitmasks — making it ideal for use in permission-based access control scenarios.

Installation

To install the package, run:

go get github.com/axkit/bitset

Features

  • Create BitSet: Initialize a new bitset with a specified size or from a hexadecimal or binary string.
  • Set/Unset Bits: Set or unset bits at specified positions.
  • Check Bits: Check if specific bits are set using either All or Any rules.
  • Convert to String: Convert the bitset to hexadecimal or binary string formats.
  • Clone: Create a deep copy of an existing bitset.

Example

package main

import (
    "fmt"
    "github.com/axkit/bitset"
)

func main() {
    bs := bitset.New(8)
    bs.Set(true, 0, 6)

    fmt.Println("Is bit 6 set?", bs.IsSet(6))                               // true
    fmt.Println("Binary representation:", bs.BinaryString())                // 10000010
    fmt.Println("Hex representation:", bs.String())                         // "82" 
    fmt.Println("Are bits 0 and 6 set?", bs.AreSet(bitset.All, 0, 6))       // true
    fmt.Println("Are bits 1, 6 or 7 set?", bs.AreSet(bitset.Any, 1, 6, 7))  // true
}
BitSet Interface

Using uint64 instead of byte and leveraging SIMD instructions can speed up bit array operations, which is why the BitSet interface is designed to allow for future optimizations.

type BitSet interface {
    IsSet(bit uint) bool
    AreSet(rule CompareRule, bits ...uint) bool
    Set(val bool, bits ...uint)
    Len() uint              
    String() string         
    BinaryString() string   
}

Usage

Creating a New BitSet

You can create a new BitSet by specifying the number of bits to allocate:

bs := bitset.New(16) // Creates a BitSet with space for 16 bits

Alternatively, you can initialize it from a hexadecimal string:

bs, err := bitset.ParseString("1с")
Setting and Checking Bits
bs.Set(true, 0, 6) // Sets bits 0 and 6
if bs.IsSet(6) {
    fmt.Println("Bit 6 is set")   
}
Checking Multiple Bits

You can check if all or any bits are set using the All or Any rules:

if bs.AreSet(bitset.All, 0, 6) {
    fmt.Println("Bits 0 and 6 are both set")
}

if bs.AreSet(bitset.Any, 0, 7) {
    fmt.Println("Bit 0 is set")
}
Quick Checking

This feature allows you to validate bits directly from a hexadecimal bitmask without creating intermediate objects. It's designed for scenarios where performance and simplicity are critical, such as validating permissions in a compact format.

// perms =  "a1ff90e428c7"

isAllowed, err := bitset.AreSet(perms, bitset.Any, 13, 42, 89)

This approach minimizes overhead and ensures rapid permission checks, making it ideal for high-performance applications.

Converting to String Representations

To get the hexadecimal or binary string representation of the bitset:

hexStr := bs.String()
fmt.Println("Hexadecimal:", hexStr)

binaryStr := bs.BinaryString()
fmt.Println("Binary:", binaryStr)

Errors

  • ErrInvalidSourceString - an invalid source string, such as an odd-length string in hexadecimal input.
  • ErrParseFailed - Returned by parsing functions when an invalid character is encountered.

Licensing

This project is licensed under the MIT License. See the LICENSE file for details.

Documentation

Overview

The package provides typical bitmap storage and manipulation functions. BitSet is a data structure that holds a set of bits, where each bit is represented by a single bit in a byte slice. The first bit is stored in the most significant bit of the first byte, and the last bit is stored in the least significant bit of the last byte. Examples:

1000 0010: Bits 0 and 6 are set. The hexadecimal representation is "82". 1000 0011: Bits 0, 6, and 7 are set. The hexadecimal representation is "83". 1100 0001 1100 0011: Bits 0, 1, 7, 8, 9, 14, and 15 are set. The hexadecimal representation is "c1c3".

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrParseFailed         = errors.New("invalid character found")
	ErrInvalidSourceString = errors.New("invalid source string")
)

ErrParseFailed is returned by the Parse function when an invalid character is found in the input string. Valid characters are 0-9, a-f, A-F.

Functions

func AreSet

func AreSet(hexStr []byte, rule CompareRule, bits ...uint) (bool, error)

AreSet evaluates whether all or any specified bits are set, based on the rule, using a hexadecimal string representation of the bitset as input.

func AreSetHexStr added in v1.0.4

func AreSetHexStr(hexStr string, rule CompareRule, bits ...uint) (bool, error)

AreSetHexStr evaluates whether all or any specified bits are set, based on the rule, using a hexadecimal string representation of the bitset as input.

func Validate added in v1.0.0

func Validate(buf []byte) error

Validate quickly checks if the input byte slice is a valid hexadecimal representation of a BitSet.

Types

type BitSet

type BitSet interface {
	IsSet(pos uint) bool
	AreSet(rule CompareRule, bitpos ...uint) bool
	Set(val bool, bitpos ...uint)
	Len() uint
	String() string
	BinaryString() string
	Bytes() []byte
}

BitSet defines an interface for manipulating a set of bits. It provides methods to check, set, retrieve, and convert the bit set.

type ByteBitSet added in v1.0.0

type ByteBitSet struct {
	// contains filtered or unexported fields
}

ByteBitSet is a BitSet implementation that stores bits in a byte slice. Each bit is represented by a single bit in the byte array, starting from the most significant bit of the first byte.

func Clone added in v0.0.2

func Clone(src ByteBitSet) ByteBitSet

Clone returns a deep copy of the provided ByteBitSet.

func New

func New(size int) ByteBitSet

New returns a new ByteBitSet with enough space to store the specified number of bits.

func ParseBinaryString added in v1.0.2

func ParseBinaryString(src string) (ByteBitSet, error)

ParseBinaryString creates a BitSet from a binary string of '0' and '1' characters. Returns an error if any characters other than '0' or '1' are found.

func ParseHexBytes added in v1.0.2

func ParseHexBytes(hexStr []byte) (ByteBitSet, error)

ParseHexBytes is a sugar function that creates a ByteBitSet from a byte slice.

func ParseHexString added in v1.0.2

func ParseHexString(hexStr string) (ByteBitSet, error)

ParseHexString creates a ByteBitSet from a hexadecimal string representation. Returns an error if the input string is invalid.

func (ByteBitSet) AreSet added in v1.0.0

func (bbs ByteBitSet) AreSet(rule CompareRule, bits ...uint) bool

AreSet checks whether all or any of the specified bits are set, depending on the rule provided. Use IsSet to check a single bit.

func (ByteBitSet) BinaryString added in v1.0.0

func (bbs ByteBitSet) BinaryString() string

BinaryString returns the binary string representation of the bitset. The leftmost bit corresponds to the lowest index.

func (ByteBitSet) Bytes added in v1.0.0

func (bbs ByteBitSet) Bytes() []byte

Bytes returns the underlying byte slice representing the bitset.

func (ByteBitSet) IsSet added in v1.0.0

func (bbs ByteBitSet) IsSet(bit uint) bool

IsSet returns true if the bit at the specified position is set to 1 or false if it is 0.

If the position is out of bounds, it returns false. Use Len() to check the size of the BitSet.

func (ByteBitSet) Len added in v1.0.0

func (bbs ByteBitSet) Len() uint

Len returns the total number of bits currently allocated in the bit set.

func (*ByteBitSet) Set added in v1.0.0

func (bbs *ByteBitSet) Set(val bool, bits ...uint)

Set updates the bits at the specified positions to the given value (true to set, false to clear). Automatically expands the internal byte slice if necessary.

func (ByteBitSet) String added in v1.0.0

func (bbs ByteBitSet) String() string

String returns the hexadecimal string representation of the bitset.

type CompareRule added in v1.0.0

type CompareRule int8

CompareRule defines the rule for comparing bits in a BitSet. It is used to determine whether all or any of the specified bits are set.

The following constants are available:

  • All: All specified bits must be set.
  • Any: At least one of the specified bits must be set.
const (
	All CompareRule = iota
	Any
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL