123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- // Package bitmap provides a datatype for long vectors of bits.
- package bitmap
- import (
- "encoding/binary"
- "encoding/json"
- "errors"
- "fmt"
- )
- // block sequence constants
- // If needed we can think of making these configurable
- const (
- blockLen = uint32(32)
- blockBytes = uint64(blockLen / 8)
- blockMAX = uint32(1<<blockLen - 1)
- blockFirstBit = uint32(1) << (blockLen - 1)
- invalidPos = uint64(0xFFFFFFFFFFFFFFFF)
- )
- var (
- // ErrNoBitAvailable is returned when no more bits are available to set
- ErrNoBitAvailable = errors.New("no bit available")
- // ErrBitAllocated is returned when the specific bit requested is already set
- ErrBitAllocated = errors.New("requested bit is already allocated")
- )
- // https://github.com/golang/go/issues/8005#issuecomment-190753527
- type noCopy struct{}
- func (noCopy) Lock() {}
- // Bitmap is a fixed-length bit vector. It is not safe for concurrent use.
- //
- // The data is stored as a list of run-length encoded blocks. It operates
- // directly on the encoded representation, without decompressing.
- type Bitmap struct {
- bits uint64
- unselected uint64
- head *sequence
- curr uint64
- // Shallow copies would share the same head pointer but a copy of the
- // unselected count. Mutating the sequence through one would change the
- // bits for all copies but only update that one copy's unselected count,
- // which would result in subtle bugs.
- noCopy noCopy
- }
- // NewHandle returns a new Bitmap of ordinals in the interval [0, n).
- func New(n uint64) *Bitmap {
- return &Bitmap{
- bits: n,
- unselected: n,
- head: &sequence{
- block: 0x0,
- count: getNumBlocks(n),
- },
- }
- }
- // Copy returns a deep copy of b.
- func Copy(b *Bitmap) *Bitmap {
- return &Bitmap{
- bits: b.bits,
- unselected: b.unselected,
- head: b.head.getCopy(),
- curr: b.curr,
- }
- }
- // sequence represents a recurring sequence of 32 bits long bitmasks
- type sequence struct {
- block uint32 // block is a symbol representing 4 byte long allocation bitmask
- count uint64 // number of consecutive blocks (symbols)
- next *sequence // next sequence
- }
- // String returns a string representation of the block sequence starting from this block
- func (s *sequence) toString() string {
- var nextBlock string
- if s.next == nil {
- nextBlock = "end"
- } else {
- nextBlock = s.next.toString()
- }
- return fmt.Sprintf("(0x%x, %d)->%s", s.block, s.count, nextBlock)
- }
- // GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence
- func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
- if s.block == blockMAX || s.count == 0 {
- return invalidPos, invalidPos, ErrNoBitAvailable
- }
- bits := from
- bitSel := blockFirstBit >> from
- for bitSel > 0 && s.block&bitSel != 0 {
- bitSel >>= 1
- bits++
- }
- // Check if the loop exited because it could not
- // find any available bit int block starting from
- // "from". Return invalid pos in that case.
- if bitSel == 0 {
- return invalidPos, invalidPos, ErrNoBitAvailable
- }
- return bits / 8, bits % 8, nil
- }
- // GetCopy returns a copy of the linked list rooted at this node
- func (s *sequence) getCopy() *sequence {
- n := &sequence{block: s.block, count: s.count}
- pn := n
- ps := s.next
- for ps != nil {
- pn.next = &sequence{block: ps.block, count: ps.count}
- pn = pn.next
- ps = ps.next
- }
- return n
- }
- // Equal checks if this sequence is equal to the passed one
- func (s *sequence) equal(o *sequence) bool {
- this := s
- other := o
- for this != nil {
- if other == nil {
- return false
- }
- if this.block != other.block || this.count != other.count {
- return false
- }
- this = this.next
- other = other.next
- }
- return other == nil
- }
- // ToByteArray converts the sequence into a byte array
- func (s *sequence) toByteArray() ([]byte, error) {
- var bb []byte
- p := s
- b := make([]byte, 12)
- for p != nil {
- binary.BigEndian.PutUint32(b[0:], p.block)
- binary.BigEndian.PutUint64(b[4:], p.count)
- bb = append(bb, b...)
- p = p.next
- }
- return bb, nil
- }
- // fromByteArray construct the sequence from the byte array
- func (s *sequence) fromByteArray(data []byte) error {
- l := len(data)
- if l%12 != 0 {
- return fmt.Errorf("cannot deserialize byte sequence of length %d (%v)", l, data)
- }
- p := s
- i := 0
- for {
- p.block = binary.BigEndian.Uint32(data[i : i+4])
- p.count = binary.BigEndian.Uint64(data[i+4 : i+12])
- i += 12
- if i == l {
- break
- }
- p.next = &sequence{}
- p = p.next
- }
- return nil
- }
- // SetAnyInRange sets the first unset bit in the range [start, end] and returns
- // the ordinal of the set bit.
- //
- // When serial=true, the bitmap is scanned starting from the ordinal following
- // the bit most recently set by [Bitmap.SetAny] or [Bitmap.SetAnyInRange].
- func (h *Bitmap) SetAnyInRange(start, end uint64, serial bool) (uint64, error) {
- if end < start || end >= h.bits {
- return invalidPos, fmt.Errorf("invalid bit range [%d, %d)", start, end)
- }
- if h.Unselected() == 0 {
- return invalidPos, ErrNoBitAvailable
- }
- return h.set(0, start, end, true, false, serial)
- }
- // SetAny sets the first unset bit in the sequence and returns the ordinal of
- // the set bit.
- //
- // When serial=true, the bitmap is scanned starting from the ordinal following
- // the bit most recently set by [Bitmap.SetAny] or [Bitmap.SetAnyInRange].
- func (h *Bitmap) SetAny(serial bool) (uint64, error) {
- if h.Unselected() == 0 {
- return invalidPos, ErrNoBitAvailable
- }
- return h.set(0, 0, h.bits-1, true, false, serial)
- }
- // Set atomically sets the corresponding bit in the sequence
- func (h *Bitmap) Set(ordinal uint64) error {
- if err := h.validateOrdinal(ordinal); err != nil {
- return err
- }
- _, err := h.set(ordinal, 0, 0, false, false, false)
- return err
- }
- // Unset atomically unsets the corresponding bit in the sequence
- func (h *Bitmap) Unset(ordinal uint64) error {
- if err := h.validateOrdinal(ordinal); err != nil {
- return err
- }
- _, err := h.set(ordinal, 0, 0, false, true, false)
- return err
- }
- // IsSet atomically checks if the ordinal bit is set. In case ordinal
- // is outside of the bit sequence limits, false is returned.
- func (h *Bitmap) IsSet(ordinal uint64) bool {
- if err := h.validateOrdinal(ordinal); err != nil {
- return false
- }
- _, _, err := checkIfAvailable(h.head, ordinal)
- return err != nil
- }
- // set/reset the bit
- func (h *Bitmap) set(ordinal, start, end uint64, any bool, release bool, serial bool) (uint64, error) {
- var (
- bitPos uint64
- bytePos uint64
- ret uint64
- err error
- )
- curr := uint64(0)
- if serial {
- curr = h.curr
- }
- // Get position if available
- if release {
- bytePos, bitPos = ordinalToPos(ordinal)
- } else {
- if any {
- bytePos, bitPos, err = getAvailableFromCurrent(h.head, start, curr, end)
- ret = posToOrdinal(bytePos, bitPos)
- if err == nil {
- h.curr = ret + 1
- }
- } else {
- bytePos, bitPos, err = checkIfAvailable(h.head, ordinal)
- ret = ordinal
- }
- }
- if err != nil {
- return ret, err
- }
- h.head = pushReservation(bytePos, bitPos, h.head, release)
- if release {
- h.unselected++
- } else {
- h.unselected--
- }
- return ret, nil
- }
- // checks is needed because to cover the case where the number of bits is not a multiple of blockLen
- func (h *Bitmap) validateOrdinal(ordinal uint64) error {
- if ordinal >= h.bits {
- return errors.New("bit does not belong to the sequence")
- }
- return nil
- }
- // MarshalBinary encodes h into a binary representation.
- func (h *Bitmap) MarshalBinary() ([]byte, error) {
- ba := make([]byte, 16)
- binary.BigEndian.PutUint64(ba[0:], h.bits)
- binary.BigEndian.PutUint64(ba[8:], h.unselected)
- bm, err := h.head.toByteArray()
- if err != nil {
- return nil, fmt.Errorf("failed to serialize head: %v", err)
- }
- ba = append(ba, bm...)
- return ba, nil
- }
- // UnmarshalBinary decodes a binary representation of a Bitmap value which was
- // generated using [Bitmap.MarshalBinary].
- //
- // The scan position for serial [Bitmap.SetAny] and [Bitmap.SetAnyInRange]
- // operations is neither unmarshaled nor reset.
- func (h *Bitmap) UnmarshalBinary(ba []byte) error {
- if ba == nil {
- return errors.New("nil byte array")
- }
- nh := &sequence{}
- err := nh.fromByteArray(ba[16:])
- if err != nil {
- return fmt.Errorf("failed to deserialize head: %v", err)
- }
- h.head = nh
- h.bits = binary.BigEndian.Uint64(ba[0:8])
- h.unselected = binary.BigEndian.Uint64(ba[8:16])
- return nil
- }
- // Bits returns the length of the bit sequence
- func (h *Bitmap) Bits() uint64 {
- return h.bits
- }
- // Unselected returns the number of bits which are not selected
- func (h *Bitmap) Unselected() uint64 {
- return h.unselected
- }
- func (h *Bitmap) String() string {
- return fmt.Sprintf("Bits: %d, Unselected: %d, Sequence: %s Curr:%d",
- h.bits, h.unselected, h.head.toString(), h.curr)
- }
- // MarshalJSON encodes h into a JSON message
- func (h *Bitmap) MarshalJSON() ([]byte, error) {
- b, err := h.MarshalBinary()
- if err != nil {
- return nil, err
- }
- return json.Marshal(b)
- }
- // UnmarshalJSON decodes JSON message into h
- func (h *Bitmap) UnmarshalJSON(data []byte) error {
- var b []byte
- if err := json.Unmarshal(data, &b); err != nil {
- return err
- }
- return h.UnmarshalBinary(b)
- }
- // getFirstAvailable looks for the first unset bit in passed mask starting from start
- func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
- // Find sequence which contains the start bit
- byteStart, bitStart := ordinalToPos(start)
- current, _, precBlocks, inBlockBytePos := findSequence(head, byteStart)
- // Derive the this sequence offsets
- byteOffset := byteStart - inBlockBytePos
- bitOffset := inBlockBytePos*8 + bitStart
- for current != nil {
- if current.block != blockMAX {
- // If the current block is not full, check if there is any bit
- // from the current bit in the current block. If not, before proceeding to the
- // next block node, make sure we check for available bit in the next
- // instance of the same block. Due to RLE same block signature will be
- // compressed.
- retry:
- bytePos, bitPos, err := current.getAvailableBit(bitOffset)
- if err != nil && precBlocks == current.count-1 {
- // This is the last instance in the same block node,
- // so move to the next block.
- goto next
- }
- if err != nil {
- // There are some more instances of the same block, so add the offset
- // and be optimistic that you will find the available bit in the next
- // instance of the same block.
- bitOffset = 0
- byteOffset += blockBytes
- precBlocks++
- goto retry
- }
- return byteOffset + bytePos, bitPos, err
- }
- // Moving to next block: Reset bit offset.
- next:
- bitOffset = 0
- byteOffset += (current.count * blockBytes) - (precBlocks * blockBytes)
- precBlocks = 0
- current = current.next
- }
- return invalidPos, invalidPos, ErrNoBitAvailable
- }
- // getAvailableFromCurrent will look for available ordinal from the current ordinal.
- // If none found then it will loop back to the start to check of the available bit.
- // This can be further optimized to check from start till curr in case of a rollover
- func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) {
- var bytePos, bitPos uint64
- var err error
- if curr != 0 && curr > start {
- bytePos, bitPos, err = getFirstAvailable(head, curr)
- ret := posToOrdinal(bytePos, bitPos)
- if end < ret || err != nil {
- goto begin
- }
- return bytePos, bitPos, nil
- }
- begin:
- bytePos, bitPos, err = getFirstAvailable(head, start)
- ret := posToOrdinal(bytePos, bitPos)
- if end < ret || err != nil {
- return invalidPos, invalidPos, ErrNoBitAvailable
- }
- return bytePos, bitPos, nil
- }
- // checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
- // If the ordinal is beyond the sequence limits, a negative response is returned
- func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) {
- bytePos, bitPos := ordinalToPos(ordinal)
- // Find the sequence containing this byte
- current, _, _, inBlockBytePos := findSequence(head, bytePos)
- if current != nil {
- // Check whether the bit corresponding to the ordinal address is unset
- bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
- if current.block&bitSel == 0 {
- return bytePos, bitPos, nil
- }
- }
- return invalidPos, invalidPos, ErrBitAllocated
- }
- // Given the byte position and the sequences list head, return the pointer to the
- // sequence containing the byte (current), the pointer to the previous sequence,
- // the number of blocks preceding the block containing the byte inside the current sequence.
- // If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos)
- func findSequence(head *sequence, bytePos uint64) (*sequence, *sequence, uint64, uint64) {
- // Find the sequence containing this byte
- previous := head
- current := head
- n := bytePos
- for current.next != nil && n >= (current.count*blockBytes) { // Nil check for less than 32 addresses masks
- n -= (current.count * blockBytes)
- previous = current
- current = current.next
- }
- // If byte is outside of the list, let caller know
- if n >= (current.count * blockBytes) {
- return nil, nil, 0, invalidPos
- }
- // Find the byte position inside the block and the number of blocks
- // preceding the block containing the byte inside this sequence
- precBlocks := n / blockBytes
- inBlockBytePos := bytePos % blockBytes
- return current, previous, precBlocks, inBlockBytePos
- }
- // PushReservation pushes the bit reservation inside the bitmask.
- // Given byte and bit positions, identify the sequence (current) which holds the block containing the affected bit.
- // Create a new block with the modified bit according to the operation (allocate/release).
- // Create a new sequence containing the new block and insert it in the proper position.
- // Remove current sequence if empty.
- // Check if new sequence can be merged with neighbour (previous/next) sequences.
- //
- // Identify "current" sequence containing block:
- //
- // [prev seq] [current seq] [next seq]
- //
- // Based on block position, resulting list of sequences can be any of three forms:
- //
- // block position Resulting list of sequences
- //
- // A) block is first in current: [prev seq] [new] [modified current seq] [next seq]
- // B) block is last in current: [prev seq] [modified current seq] [new] [next seq]
- // C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq]
- func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequence {
- // Store list's head
- newHead := head
- // Find the sequence containing this byte
- current, previous, precBlocks, inBlockBytePos := findSequence(head, bytePos)
- if current == nil {
- return newHead
- }
- // Construct updated block
- bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
- newBlock := current.block
- if release {
- newBlock &^= bitSel
- } else {
- newBlock |= bitSel
- }
- // Quit if it was a redundant request
- if current.block == newBlock {
- return newHead
- }
- // Current sequence inevitably looses one block, upadate count
- current.count--
- // Create new sequence
- newSequence := &sequence{block: newBlock, count: 1}
- // Insert the new sequence in the list based on block position
- if precBlocks == 0 { // First in sequence (A)
- newSequence.next = current
- if current == head {
- newHead = newSequence
- previous = newHead
- } else {
- previous.next = newSequence
- }
- removeCurrentIfEmpty(&newHead, newSequence, current)
- mergeSequences(previous)
- } else if precBlocks == current.count { // Last in sequence (B)
- newSequence.next = current.next
- current.next = newSequence
- mergeSequences(current)
- } else { // In between the sequence (C)
- currPre := &sequence{block: current.block, count: precBlocks, next: newSequence}
- currPost := current
- currPost.count -= precBlocks
- newSequence.next = currPost
- if currPost == head {
- newHead = currPre
- } else {
- previous.next = currPre
- }
- // No merging or empty current possible here
- }
- return newHead
- }
- // Removes the current sequence from the list if empty, adjusting the head pointer if needed
- func removeCurrentIfEmpty(head **sequence, previous, current *sequence) {
- if current.count == 0 {
- if current == *head {
- *head = current.next
- } else {
- previous.next = current.next
- }
- }
- }
- // Given a pointer to a sequence, it checks if it can be merged with any following sequences
- // It stops when no more merging is possible.
- // TODO: Optimization: only attempt merge from start to end sequence, no need to scan till the end of the list
- func mergeSequences(seq *sequence) {
- if seq != nil {
- // Merge all what possible from seq
- for seq.next != nil && seq.block == seq.next.block {
- seq.count += seq.next.count
- seq.next = seq.next.next
- }
- // Move to next
- mergeSequences(seq.next)
- }
- }
- func getNumBlocks(numBits uint64) uint64 {
- numBlocks := numBits / uint64(blockLen)
- if numBits%uint64(blockLen) != 0 {
- numBlocks++
- }
- return numBlocks
- }
- func ordinalToPos(ordinal uint64) (uint64, uint64) {
- return ordinal / 8, ordinal % 8
- }
- func posToOrdinal(bytePos, bitPos uint64) uint64 {
- return bytePos*8 + bitPos
- }
|