add CountRange()

This commit adds `roaring.Bitmap.CountRange()` to return the number
of bits in a subrange of a bitmap. This significantly improves
server start time and is needed for upcoming zero copy bitmap
optimizations.
This commit is contained in:
Ben Johnson 2016-08-15 16:03:25 -06:00
parent 5d48436e97
commit 650e03cc79
2 changed files with 82 additions and 1 deletions

View file

@ -234,7 +234,8 @@ func (f *Fragment) openCache() error {
// Read in all bitmaps by ID.
// This will cause them to be added to the cache.
for _, bitmapID := range pb.GetBitmapIDs() {
f.bitmap(bitmapID)
n := f.storage.CountRange(bitmapID*SliceWidth, (bitmapID+1)*SliceWidth)
f.cache.Add(bitmapID, n)
}
return nil

View file

@ -7,6 +7,7 @@ import (
"fmt"
"hash/fnv"
"io"
"sort"
"unsafe"
)
@ -134,6 +135,41 @@ func (b *Bitmap) Count() (n uint64) {
return n
}
// CountRange returns the number of bits set between [start, end).
func (b *Bitmap) CountRange(start, end uint64) (n uint64) {
i := search64(b.keys, highbits(start))
j := search64(b.keys, highbits(end))
// If range is entirely in one container then just count that range.
if i > 0 && i == j {
return uint64(b.containers[i].countRange(lowbits(start), lowbits(end)))
}
// Count first partial container.
if i < 0 {
i = -i
} else {
n += uint64(b.containers[i].countRange(lowbits(start), (bitmapN*64)+1))
}
// Count last container.
if j < 0 {
j = -j
if j > len(b.containers) {
j = len(b.containers)
}
} else {
n += uint64(b.containers[j].countRange(0, lowbits(end)))
}
// Count containers in between.
for x := i + 1; x < j; x++ {
n += uint64(b.containers[x].n)
}
return n
}
// Slice returns a slice of all integers in the bitmap.
func (b *Bitmap) Slice() []uint64 {
var a []uint64
@ -658,6 +694,50 @@ func (c *container) unmap() {
c.mapped = false
}
// countRange counts the number of bits set between [start, end).
func (c *container) countRange(start, end uint32) (n int) {
if c.isArray() {
return c.arrayCountRange(start, end)
}
return c.bitmapCountRange(start, end)
}
func (c *container) arrayCountRange(start, end uint32) (n int) {
i := sort.Search(len(c.array), func(i int) bool { return c.array[i] >= start })
for ; i < len(c.array); i++ {
v := c.array[i]
if v >= end {
break
}
n++
}
return n
}
func (c *container) bitmapCountRange(start, end uint32) int {
var n uint64
i, j := start/64, end/64
// Count partial starting word.
if off := start % 64; off != 0 {
n += popcount(c.bitmap[i] << off)
}
// Count words in between.
for ; i < j; i++ {
n += popcount(c.bitmap[i])
}
// Count partial ending word.
if int(j) < len(c.bitmap) {
if off := end % 64; off != 0 {
n += popcount(c.bitmap[j] >> off)
}
}
return int(n)
}
// add adds a value to the container.
func (c *container) add(v uint32) bool {
if c.isArray() {