| // 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, update 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 | |
| } |