Handle BitmapPtr cells in countRange

We need to be able to count bits in BitmapPtr containers. This only
comes up if you have a non-container-aligned range count, which we
never do in real production yet, but the API allows it so it should
work. In order to do this, we need to provide the tx to countRange
so it can grab pages as needed. Arguably, we should be able to avoid
actually creating/copying that page since we're only using it
internally, never returning it, but this is a pretty rare case
and probably not performance-critical.
This commit is contained in:
Seebs 2021-01-25 14:50:37 -06:00
parent d1a9c91a96
commit a238afb21a
2 changed files with 8 additions and 5 deletions

View file

@ -462,7 +462,7 @@ func (c *leafCell) lastValue(tx *Tx) uint16 {
// We have to take int32 rather than uint16 because the interval is [start, end),
// and otherwise we have no way to ask to count the entire container (the
// high bit will be missed).
func (c *leafCell) countRange(start, end int32) (n int) {
func (c *leafCell) countRange(tx *Tx, start, end int32) (n int) {
// If the full range is being queried, simply use the precalculated count.
if start == 0 && end > math.MaxUint16 {
return c.BitN
@ -475,6 +475,10 @@ func (c *leafCell) countRange(start, end int32) (n int) {
return int(roaring.RunCountRange(toInterval16(c.Data), start, end))
case ContainerTypeBitmap:
return int(roaring.BitmapCountRange(toArray64(c.Data), start, end))
case ContainerTypeBitmapPtr:
_, a, err := tx.leafCellBitmap(toPgno(c.Data))
panicOn(err)
return int(roaring.BitmapCountRange(a, start, end))
default:
panic(fmt.Sprintf("invalid container type: %d", c.Type))
}

View file

@ -1318,7 +1318,6 @@ func (tx *Tx) CountRange(name string, start, end uint64) (uint64, error) {
} else if err != nil {
return 0, err
}
var n uint64
for {
if err := csr.Next(); err == io.EOF {
@ -1341,7 +1340,7 @@ func (tx *Tx) CountRange(name string, start, end uint64) (uint64, error) {
// If range is entirely in one container then just count that range.
if skey == ekey {
return uint64(c.countRange(int32(lowbits(start)), ebits)), nil
return uint64(c.countRange(tx, int32(lowbits(start)), ebits)), nil
}
// INVAR: skey < ekey
@ -1351,7 +1350,7 @@ func (tx *Tx) CountRange(name string, start, end uint64) (uint64, error) {
break
}
if k == skey {
n += uint64(c.countRange(int32(lowbits(start)), roaring.MaxContainerVal+1))
n += uint64(c.countRange(tx, int32(lowbits(start)), roaring.MaxContainerVal+1))
continue
}
if k < ekey {
@ -1359,7 +1358,7 @@ func (tx *Tx) CountRange(name string, start, end uint64) (uint64, error) {
continue
}
if k == ekey && ebits > 0 {
n += uint64(c.countRange(0, ebits))
n += uint64(c.countRange(tx, 0, ebits))
break
}
}