Skip to content

Commit 9eab2df

Browse files
committed
Implement GetSetBits for bitArray and sparseBitArray
1 parent 42ddd6d commit 9eab2df

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

bitarray/bitarray.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ efficient way. This is *NOT* a threadsafe package.
2020
*/
2121
package bitarray
2222

23+
import "math/bits"
24+
2325
// bitArray is a struct that maintains state of a bit array.
2426
type bitArray struct {
2527
blocks []block
@@ -117,8 +119,66 @@ func (ba *bitArray) GetBit(k uint64) (bool, error) {
117119
}
118120

119121
// GetSetBits gets the position of bits set in the array.
120-
func (ba *bitArray) GetSetBits(_ uint64, buffer []uint64) []uint64 {
121-
return buffer[:0]
122+
func (ba *bitArray) GetSetBits(from uint64, buffer []uint64) []uint64 {
123+
fromBlockIndex, fromOffset := getIndexAndRemainder(from)
124+
return getSetBitsInBlocks(
125+
fromBlockIndex,
126+
fromOffset,
127+
ba.blocks[fromBlockIndex:],
128+
nil,
129+
buffer,
130+
)
131+
}
132+
133+
// getSetBitsInBlocks fills a buffer with positions of set bits in the provided blocks. Optionally, indices may be
134+
// provided for sparse/non-consecutive blocks.
135+
func getSetBitsInBlocks(
136+
fromBlockIndex, fromOffset uint64,
137+
blocks []block,
138+
indices []uint64,
139+
buffer []uint64,
140+
) []uint64 {
141+
bufferCapacity := cap(buffer)
142+
results := buffer[:bufferCapacity]
143+
resultSize := 0
144+
145+
for i, block := range blocks {
146+
blockIndex := fromBlockIndex + uint64(i)
147+
if indices != nil {
148+
blockIndex = indices[i]
149+
}
150+
151+
isFirstBlock := blockIndex == fromBlockIndex
152+
if isFirstBlock {
153+
block >>= fromOffset
154+
}
155+
156+
for block != 0 {
157+
trailing := bits.TrailingZeros64(uint64(block))
158+
159+
if isFirstBlock {
160+
results[resultSize] = uint64(trailing) + (blockIndex << 6) + fromOffset
161+
} else {
162+
results[resultSize] = uint64(trailing) + (blockIndex << 6)
163+
}
164+
resultSize++
165+
166+
if resultSize == cap(results) {
167+
return results[:resultSize]
168+
}
169+
170+
// Example of this expression:
171+
// block 01001100
172+
// ^block 10110011
173+
// (^block) + 1 10110100
174+
// block & (^block) + 1 00000100
175+
// block ^ mask 01001000
176+
mask := block & ((^block) + 1)
177+
block = block ^ mask
178+
}
179+
}
180+
181+
return results[:resultSize]
122182
}
123183

124184
// ClearBit will unset a bit at the given index if it is set.

bitarray/sparse_bitarray.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,21 @@ func (sba *sparseBitArray) GetBit(k uint64) (bool, error) {
128128
}
129129

130130
// GetSetBits gets the position of bits set in the array.
131-
func (sba *sparseBitArray) GetSetBits(_ uint64, buffer []uint64) []uint64 {
132-
return buffer[:0]
131+
func (sba *sparseBitArray) GetSetBits(from uint64, buffer []uint64) []uint64 {
132+
fromBlockIndex, fromOffset := getIndexAndRemainder(from)
133+
134+
fromBlockLocation := sba.indices.search(fromBlockIndex)
135+
if int(fromBlockLocation) == len(sba.indices) {
136+
return buffer[:0]
137+
}
138+
139+
return getSetBitsInBlocks(
140+
fromBlockIndex,
141+
fromOffset,
142+
sba.blocks[fromBlockLocation:],
143+
sba.indices[fromBlockLocation:],
144+
buffer,
145+
)
133146
}
134147

135148
// ToNums converts this sparse bitarray to a list of numbers contained

0 commit comments

Comments
 (0)