featurebase/roaring/roaring.go
Travis c3ed12c20e Refactor fragment.bitmap() so that it leverages bitmapCache and so that
it's no longer reponsible for updating the count cache.
This commit also helps SetBit/ClearBit performance by allowing them
to work against data from `bitmapCache` instead of loading bitmaps
from fragment.storage every time.
2017-03-06 11:06:22 -06:00

1841 lines
40 KiB
Go

// package roaring implements roaring bitmaps with support for incremental changes.
package roaring
import (
"encoding/binary"
"errors"
"fmt"
"hash/fnv"
"io"
"sort"
"unsafe"
)
const (
// cookie is the first four bytes in a roaring bitmap file.
cookie = uint32(12346)
// headerSize is the size of the cookie and key count at the beginning of a file.
headerSize = 4 + 4
// bitmapN is the number of values in a container.bitmap.
bitmapN = (1 << 16) / 64
// manual allocation size tuned to our average client data
manualAlloc = 524288
)
// Bitmap represents a roaring bitmap.
type Bitmap struct {
keys []uint64 // keys for containers
containers []*container // array and bitmap containers
// Number of operations written to the writer.
opN int
// Writer where operations are appended to.
OpWriter io.Writer
}
// NewBitmap returns a Bitmap with an initial set of values.
func NewBitmap(a ...uint64) *Bitmap {
b := &Bitmap{}
b.Add(a...)
return b
}
// Clone returns a heap allocated copy of the bitmap.
// Note: The OpWriter IS NOT copied to the new bitmap.
func (b *Bitmap) Clone() *Bitmap {
if b == nil {
return nil
}
// Create a copy of the bitmap structure.
other := &Bitmap{
keys: make([]uint64, len(b.keys)),
containers: make([]*container, len(b.containers)),
}
// Copy keys & clone containers.
copy(other.keys, b.keys)
for i, c := range b.containers {
other.containers[i] = c.clone()
}
return other
}
// Add adds values to the bitmap.
func (b *Bitmap) Add(a ...uint64) (changed bool, err error) {
changed = false
for _, v := range a {
// Create an add operation.
op := &op{typ: opTypeAdd, value: v}
// Write operation to op log.
if err := b.writeOp(op); err != nil {
return false, err
}
// Apply to the in-memory bitmap.
if op.apply(b) {
changed = true
}
}
return changed, nil
}
func (b *Bitmap) add(v uint64) bool {
hb := highbits(v)
i := search64(b.keys, hb)
// If index is negative then there's not an exact match
// and a container needs to be added.
if i < 0 {
b.insertAt(hb, newContainer(), -i-1)
i = -i - 1
}
return b.containers[i].add(lowbits(v))
}
// Contains returns true if v is in the bitmap.
func (b *Bitmap) Contains(v uint64) bool {
c := b.container(highbits(v))
if c == nil {
return false
}
return c.contains(lowbits(v))
}
// Remove removes values from the bitmap.
func (b *Bitmap) Remove(a ...uint64) (changed bool, err error) {
changed = false
for _, v := range a {
// Create an add operation.
op := &op{typ: opTypeRemove, value: v}
// Write operation to op log.
if err := b.writeOp(op); err != nil {
return false, err
}
// Apply operation to the bitmap.
if op.apply(b) {
changed = true
}
}
return changed, nil
}
func (b *Bitmap) remove(v uint64) bool {
hb := highbits(v)
i := search64(b.keys, hb)
if i < 0 {
return false
}
return b.containers[i].remove(lowbits(v))
}
// Max returns the highest value in the bitmap.
// Returns zero if the bitmap is empty.
func (b *Bitmap) Max() uint64 {
if len(b.keys) == 0 {
return 0
}
hb := b.keys[len(b.keys)-1]
lb := b.containers[len(b.containers)-1].max()
return uint64(hb)<<16 | uint64(lb)
}
// Count returns the number of bits set in the bitmap.
func (b *Bitmap) Count() (n uint64) {
for _, container := range b.containers {
n += uint64(container.n)
}
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
itr := b.Iterator()
itr.Seek(0)
for v, eof := itr.Next(); !eof; v, eof = itr.Next() {
a = append(a, v)
}
return a
}
// SliceRange returns a slice of integers between [start, end).
func (b *Bitmap) SliceRange(start, end uint64) []uint64 {
var a []uint64
itr := b.Iterator()
itr.Seek(start)
for v, eof := itr.Next(); !eof && v < end; v, eof = itr.Next() {
a = append(a, v)
}
return a
}
// ForEach executes fn for each value in the bitmap.
func (b *Bitmap) ForEach(fn func(uint64)) {
itr := b.Iterator()
itr.Seek(0)
for v, eof := itr.Next(); !eof; v, eof = itr.Next() {
fn(v)
}
}
// ForEachRange executes fn for each value in the bitmap between [start, end).
func (b *Bitmap) ForEachRange(start, end uint64, fn func(uint64)) {
itr := b.Iterator()
itr.Seek(start)
for v, eof := itr.Next(); !eof && v < end; v, eof = itr.Next() {
fn(v)
}
}
// OffsetRange returns a new bitmap with a containers offset by start.
func (b *Bitmap) OffsetRange(offset, start, end uint64) *Bitmap {
if lowbits(offset) != 0 {
panic("offset must not contain low bits")
}
if lowbits(start) != 0 {
panic("range start must not contain low bits")
}
if lowbits(end) != 0 {
panic("range end must not contain low bits")
}
off := highbits(offset)
hi0, hi1 := highbits(start), highbits(end)
// Find starting container.
n := len(b.containers)
i := sort.Search(n, func(i int) bool { return b.keys[i] >= hi0 })
var other Bitmap
for ; i < n; i++ {
key := b.keys[i]
// If we've exceeded the upper bound then exit.
if key >= hi1 {
break
}
// Otherwise append container with offset key.
other.keys = append(other.keys, off+(key-hi0))
other.containers = append(other.containers, b.containers[i])
}
return &other
}
// container returns the container with the given key.
func (b *Bitmap) container(key uint64) *container {
i := search64(b.keys, key)
if i < 0 {
return nil
}
return b.containers[i]
}
func insertU64(original []uint64, position int, value uint64) []uint64 {
l := len(original)
target := original
if cap(original) == l {
target = make([]uint64, l+1, l+manualAlloc)
copy(target, original[:position])
} else {
target = append(target, 0)
}
copy(target[position+1:], original[position:])
target[position] = value
return target
}
func insertContainer(original []*container, position int, value *container) []*container {
l := len(original)
target := original
if cap(original) == l {
target = make([]*container, l+1, l+manualAlloc)
copy(target, original[:position])
} else {
target = append(target, nil)
}
copy(target[position+1:], original[position:])
target[position] = value
return target
}
func (b *Bitmap) insertAt(key uint64, c *container, i int) {
b.keys = insertU64(b.keys, i, key)
b.containers = insertContainer(b.containers, i, c)
}
// IntersectionCount returns the number of intersections between b and other.
func (b *Bitmap) IntersectionCount(other *Bitmap) uint64 {
var n uint64
for i, j := 0, 0; i < len(b.containers) && j < len(other.containers); {
ki, kj := b.keys[i], other.keys[j]
if ki < kj {
i++
} else if ki > kj {
j++
} else {
n += intersectionCount(b.containers[i], other.containers[j])
i, j = i+1, j+1
}
}
return n
}
// Intersect returns the intersection of b and other.
func (b *Bitmap) Intersect(other *Bitmap) *Bitmap {
output := &Bitmap{}
ki, ci := b.keys, b.containers
kj, cj := other.keys, other.containers
for {
var key uint64
var container *container
ni, nj := len(ki), len(kj)
if ni == 0 && nj == 0 { // eof(i,j)
break
} else if ni == 0 || (nj != 0 && ki[0] > kj[0]) { // eof(i) or i > j
key, container = kj[0], cj[0].clone()
kj, cj = kj[1:], cj[1:]
} else if nj == 0 || (ki[0] < kj[0]) { // eof(j) or i < j
key, container = ki[0], ci[0].clone()
ki, ci = ki[1:], ci[1:]
} else { // i == j
key, container = ki[0], intersect(ci[0], cj[0])
ki, ci = ki[1:], ci[1:]
kj, cj = kj[1:], cj[1:]
output.keys = append(output.keys, key)
output.containers = append(output.containers, container)
}
}
return output
}
// Union returns the bitwise union of b and other.
func (b *Bitmap) Union(other *Bitmap) *Bitmap {
output := &Bitmap{}
ki, ci := b.keys, b.containers
kj, cj := other.keys, other.containers
for {
var key uint64
var container *container
ni, nj := len(ki), len(kj)
if ni == 0 && nj == 0 { // eof(i,j)
break
} else if ni == 0 || (nj != 0 && ki[0] > kj[0]) { // eof(i) or i > j
key, container = kj[0], cj[0].clone()
kj, cj = kj[1:], cj[1:]
} else if nj == 0 || (ki[0] < kj[0]) { // eof(j) or i < j
key, container = ki[0], ci[0].clone()
ki, ci = ki[1:], ci[1:]
} else { // i == j
key, container = ki[0], union(ci[0], cj[0])
ki, ci = ki[1:], ci[1:]
kj, cj = kj[1:], cj[1:]
}
output.keys = append(output.keys, key)
output.containers = append(output.containers, container)
}
return output
}
// Difference returns the difference of b and other.
func (b *Bitmap) Difference(other *Bitmap) *Bitmap {
output := &Bitmap{}
ki, ci := b.keys, b.containers
kj, cj := other.keys, other.containers
for {
var key uint64
var container *container
ni, nj := len(ki), len(kj)
if ni == 0 { // eof(i)
break
} else if nj == 0 || ki[0] < kj[0] { // eof(j) or i < j
key, container = ki[0], ci[0].clone()
ki, ci = ki[1:], ci[1:]
output.keys = append(output.keys, key)
output.containers = append(output.containers, container)
} else if nj > 0 && ki[0] > kj[0] { // i > j
kj, cj = kj[1:], cj[1:]
} else { // i == j
key, container = ki[0], difference(ci[0], cj[0])
ki, ci = ki[1:], ci[1:]
kj, cj = kj[1:], cj[1:]
output.keys = append(output.keys, key)
output.containers = append(output.containers, container)
}
}
return output
}
// removeEmptyContainers deletes all containers that have a count of zero.
func (b *Bitmap) removeEmptyContainers() {
for i := 0; i < len(b.containers); {
c := b.containers[i]
if c.n == 0 {
b.keys = append(b.keys[:i], b.keys[i+1:]...)
copy(b.containers[i:], b.containers[i+1:])
b.containers[len(b.containers)-1] = nil
b.containers = b.containers[:len(b.containers)-1]
continue
}
i++
}
}
func (b *Bitmap) countEmptyContainers() int {
result := 0
for i := 0; i < len(b.containers); {
c := b.containers[i]
if c.n == 0 {
result++
}
i++
}
return result
}
// WriteTo writes b to w.
func (b *Bitmap) WriteTo(w io.Writer) (n int64, err error) {
// Remove empty containers before persisting.
//b.removeEmptyContainers()
containerCount := len(b.keys) - b.countEmptyContainers()
// Build header before writing individual container blocks.
buf := make([]byte, headerSize+(containerCount*(4+8+4)))
binary.LittleEndian.PutUint32(buf[0:], cookie)
binary.LittleEndian.PutUint32(buf[4:], uint32(containerCount))
empty := 0
// Encode keys and cardinality.
for i, key := range b.keys {
c := b.containers[i]
// Verify container count before writing.
// TODO: instead of commenting this out, we need to make it a configuration option
//count := c.count()
//assert(c.count() == c.n, "cannot write container count, mismatch: count=%d, n=%d", count, c.n)
if c.n > 0 {
binary.LittleEndian.PutUint64(buf[headerSize+(i-empty)*12:], uint64(key))
binary.LittleEndian.PutUint32(buf[headerSize+(i-empty)*12+8:], uint32(c.n-1))
} else {
empty++
}
}
// Write the offset for each container block.
offset := uint32(len(buf))
empty = 0
for i, c := range b.containers {
if c.n > 0 {
binary.LittleEndian.PutUint32(buf[headerSize+(containerCount*12)+((i-empty)*4):], uint32(offset))
} else {
empty++
}
offset += uint32(c.size())
}
// Write header.
i, err := w.Write(buf)
n += int64(i)
if err != nil {
return n, err
}
// Write each container block.
for _, c := range b.containers {
if c.n > 0 {
nn, err := c.WriteTo(w)
n += nn
if err != nil {
return n, err
}
}
}
return n, nil
}
// UnmarshalBinary decodes b from a binary-encoded byte slice.
func (b *Bitmap) UnmarshalBinary(data []byte) error {
if len(data) < headerSize {
return errors.New("data too small")
}
// Verify the first 4 bytes are the correct cookie.
if v := binary.LittleEndian.Uint32(data[0:4]); v != cookie {
return errors.New("invalid roaring file")
}
// Read key count.
keyN := binary.LittleEndian.Uint32(data[4:8])
b.keys = make([]uint64, keyN)
b.containers = make([]*container, keyN)
// Read container key headers.
for i, buf := 0, data[8:]; i < int(keyN); i, buf = i+1, buf[12:] {
b.keys[i] = binary.LittleEndian.Uint64(buf[0:8])
b.containers[i] = &container{
n: int(binary.LittleEndian.Uint32(buf[8:12])) + 1,
mapped: true,
}
}
// Read container offsets and attach data.
opsOffset := 8 + int(keyN)*12
for i, buf := 0, data[opsOffset:]; i < int(keyN); i, buf = i+1, buf[4:] {
offset := binary.LittleEndian.Uint32(buf[0:4])
// Verify the offset is within the bounds of the input data.
if int(offset) >= len(data) {
return fmt.Errorf("offset out of bounds: off=%d, len=%d", offset, len(data))
}
// Map byte slice directly to the container data.
c := b.containers[i]
if c.n <= ArrayMaxSize {
c.array = (*[0xFFFFFFF]uint32)(unsafe.Pointer(&data[offset]))[:c.n]
// TODO: instead of commenting this out, we need to make it a configuration option
//for _, v := range c.array {
// assert(lowbits(uint64(v)) == v, "array value out of range: %d", v)
//}
opsOffset = int(offset) + len(c.array)*4
} else {
c.bitmap = (*[0xFFFFFFF]uint64)(unsafe.Pointer(&data[offset]))[:bitmapN]
opsOffset = int(offset) + len(c.bitmap)*8
}
// Verify container count on load.
// TODO: instead of commenting this out, we need to make it a configuration option
//count := c.count()
//assert(c.count() == c.n, "container count mismatch: count=%d, n=%d", count, c.n)
}
// Read ops log until the end of the file.
buf := data[opsOffset:]
for {
// Exit when there are no more ops to parse.
if len(buf) == 0 {
break
}
// Unmarshal the op and apply it.
var op op
if err := op.UnmarshalBinary(buf); err != nil {
// FIXME(benbjohnson): return error with position so file can be trimmed.
return err
}
op.apply(b)
// Increase the op count.
b.opN++
// Move the buffer forward.
buf = buf[op.size():]
}
return nil
}
// writeOp writes op to the OpWriter, if available.
func (b *Bitmap) writeOp(op *op) error {
if b.OpWriter == nil {
return nil
}
if _, err := op.WriteTo(b.OpWriter); err != nil {
return err
}
b.opN++
return nil
}
// Iterator returns a new iterator for the bitmap.
func (b *Bitmap) Iterator() *Iterator {
itr := &Iterator{bitmap: b}
itr.Seek(0)
return itr
}
// Info returns stats for the bitmap.
func (b *Bitmap) Info() BitmapInfo {
info := BitmapInfo{
OpN: b.opN,
Containers: make([]ContainerInfo, len(b.containers)),
}
for i, c := range b.containers {
ci := c.info()
ci.Key = b.keys[i]
info.Containers[i] = ci
}
return info
}
// Check performs a consistency check on the bitmap. Returns nil if consistent.
func (b *Bitmap) Check() error {
var a ErrorList
// Check keys/containers match. Return immediately if this happens.
if len(b.keys) != len(b.containers) {
a.Append(fmt.Errorf("key/container count mismatch: %d != %d", len(b.keys), len(b.containers)))
return a
}
// Check each container.
for i, c := range b.containers {
if err := c.check(); err != nil {
a.AppendWithPrefix(err, fmt.Sprintf("%d/", b.keys[i]))
}
}
if len(a) == 0 {
return nil
}
return a
}
// BitmapInfo represents a point-in-time snapshot of bitmap stats.
type BitmapInfo struct {
OpN int
Containers []ContainerInfo
}
// Iterator represents an iterator over a Bitmap.
type Iterator struct {
bitmap *Bitmap
i, j int
}
// eof returns true if the iterator is at the end of the bitmap.
func (itr *Iterator) eof() bool { return itr.i >= len(itr.bitmap.containers) }
// Seek moves to the first value equal to or greater than v.
func (itr *Iterator) Seek(seek uint64) {
// Move to the correct container.
itr.i = search64(itr.bitmap.keys, highbits(seek))
if itr.i < 0 {
itr.i = -itr.i - 1
}
if itr.eof() {
return
}
// Move to the correct value index inside the array container.
lb := lowbits(seek)
if c := itr.bitmap.containers[itr.i]; c.isArray() {
// Find index in the container.
itr.j = search32(c.array, lb)
if itr.j < 0 {
itr.j = -itr.j - 1
}
if itr.j < len(c.array) {
itr.j--
return
}
// If it's at the end of the container then move to the next one.
itr.i, itr.j = itr.i+1, -1
return
}
// If it's a bitmap container then move to index before the value and call next().
itr.j = int(lb) - 1
}
// Next returns the next value in the bitmap.
// Returns eof as true if there are no values left in the iterator.
func (itr *Iterator) Next() (v uint64, eof bool) {
// Iterate over containers until we find the next value or EOF.
for {
if itr.eof() {
return 0, true
}
// Move to the next item in the container if it's an array container.
c := itr.bitmap.containers[itr.i]
if c.isArray() {
if itr.j >= c.n-1 {
itr.i, itr.j = itr.i+1, -1
continue
}
itr.j++
return itr.peek(), false
}
// Move to the next possible index in the bitmap container.
itr.j++
// Find first non-zero bit in current bitmap, if possible.
hb := int(itr.j / 64)
if hb >= len(c.bitmap) {
itr.i, itr.j = itr.i+1, -1
continue
}
lb := c.bitmap[hb] >> (uint(itr.j) % 64)
if lb != 0 {
itr.j = int(itr.j) + trailingZeroN(lb)
return itr.peek(), false
}
// Otherwise iterate through remaining bitmaps to find next bit.
for hb++; hb < len(c.bitmap); hb++ {
if c.bitmap[hb] != 0 {
itr.j = int(hb*64) + trailingZeroN(c.bitmap[hb])
return itr.peek(), false
}
}
// If no bits found then move to the next container.
itr.i, itr.j = itr.i+1, -1
}
}
// peek returns the current value.
func (itr *Iterator) peek() uint64 {
key := itr.bitmap.keys[itr.i]
c := itr.bitmap.containers[itr.i]
if c.isArray() {
return uint64(key)<<16 | uint64(c.array[itr.j])
}
return uint64(key)<<16 | uint64(itr.j)
}
// BufIterator wraps an iterator to provide the ability to unread values.
type BufIterator struct {
buf struct {
v uint64
eof bool
full bool
}
itr *Iterator
}
// NewBufIterator returns a buffered iterator that wraps itr.
func NewBufIterator(itr *Iterator) *BufIterator {
return &BufIterator{itr: itr}
}
// Seek moves to the first pair equal to or greater than pseek/bseek.
func (itr *BufIterator) Seek(v uint64) {
itr.buf.full = false
itr.itr.Seek(v)
}
// Next returns the next pair in the bitmap.
// If a value has been buffered then it is returned and the buffer is cleared.
func (itr *BufIterator) Next() (v uint64, eof bool) {
if itr.buf.full {
itr.buf.full = false
return itr.buf.v, itr.buf.eof
}
// Read value onto buffer in case of unread.
itr.buf.v, itr.buf.eof = itr.itr.Next()
return itr.buf.v, itr.buf.eof
}
// Peek reads the next value but leaves it on the buffer.
func (itr *BufIterator) Peek() (v uint64, eof bool) {
v, eof = itr.Next()
itr.Unread()
return
}
// Unread pushes previous pair on to the buffer.
// Panics if the buffer is already full.
func (itr *BufIterator) Unread() {
if itr.buf.full {
panic("roaring.BufIterator: buffer full")
}
itr.buf.full = true
}
// The maximum size of array containers.
const ArrayMaxSize = 4096
// container represents a container for uint32 integers.
//
// These are used for storing the low bits. Containers are separated into two
// types depending on cardinality. For containers with less than 4,096 values,
// an array container is used. For containers with more than 4,096 values,
// the values are encoded into bitmaps.
type container struct {
n int // number of integers in container
array []uint32 // used for array containers
bitmap []uint64 // used for bitmap containers
mapped bool // mapped directly to a byte slice when true
}
// newContainer returns a new instance of container.
func newContainer() *container {
return &container{}
}
// isArray returns true if the container is an array container.
func (c *container) isArray() bool { return c.bitmap == nil }
// unmap creates copies of the containers data in the heap.
//
// This is performed when altering the container since its contents could be
// pointing at a read-only mmap.
func (c *container) unmap() {
if !c.mapped {
return
}
if c.array != nil {
tmp := make([]uint32, len(c.array))
copy(tmp, c.array)
c.array = tmp
}
if c.bitmap != nil {
tmp := make([]uint64, len(c.bitmap))
copy(tmp, c.bitmap)
c.bitmap = tmp
}
c.mapped = false
}
// count counts all bits in the container.
func (c *container) count() (n int) {
return c.countRange(0, (bitmapN*64)+1)
}
// 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() {
return c.arrayAdd(v)
}
return c.bitmapAdd(v)
}
func (c *container) arrayAdd(v uint32) bool {
// Optimize appending to the end of an array container.
if c.n > 0 && c.n < ArrayMaxSize && c.isArray() && c.array[c.n-1] < v {
c.unmap()
c.array = append(c.array, v)
c.n++
return true
}
// Find index of the integer in the container. Exit if it already exists.
i := search32(c.array, v)
if i >= 0 {
return false
}
// Convert to a bitmap container if too many values are in an array container.
if c.n >= ArrayMaxSize {
c.convertToBitmap()
return c.bitmapAdd(v)
}
// Otherwise insert into array.
c.unmap()
i = -i - 1
c.array = append(c.array, 0)
copy(c.array[i+1:], c.array[i:])
c.array[i] = v
c.n++
return true
}
func (c *container) bitmapAdd(v uint32) bool {
if c.bitmapContains(v) {
return false
}
c.unmap()
c.bitmap[v/64] |= (1 << uint64(v%64))
c.n++
return true
}
// contains returns true if v is in the container.
func (c *container) contains(v uint32) bool {
if c.isArray() {
return c.arrayContains(v)
}
return c.bitmapContains(v)
}
func (c *container) arrayContains(v uint32) bool {
return search32(c.array, v) >= 0
}
func (c *container) bitmapContains(v uint32) bool {
return (c.bitmap[v/64] & (1 << uint64(v%64))) != 0
}
// remove adds a value to the container.
func (c *container) remove(v uint32) bool {
if c.isArray() {
return c.arrayRemove(v)
}
return c.bitmapRemove(v)
}
func (c *container) arrayRemove(v uint32) bool {
i := search32(c.array, v)
if i < 0 {
return false
}
c.unmap()
c.n--
c.array = append(c.array[:i], c.array[i+1:]...)
return true
}
func (c *container) bitmapRemove(v uint32) bool {
if !c.bitmapContains(v) {
return false
}
c.unmap()
// Lower count and remove element.
c.n--
c.bitmap[v/64] &^= (uint64(1) << (v % 64))
// Convert to array if we go below the threshold.
if c.n == ArrayMaxSize {
c.convertToArray()
}
return true
}
// max returns the maximum value in the container.
func (c *container) max() uint32 {
if c.isArray() {
return c.arrayMax()
}
return c.bitmapMax()
}
func (c *container) arrayMax() uint32 {
if len(c.array) == 0 {
return 0 //probably hiding some ugly bug but it prevents a crash
}
return c.array[len(c.array)-1]
}
func (c *container) bitmapMax() uint32 {
// Search bitmap in reverse order.
for i := len(c.bitmap) - 1; i >= 0; i-- {
// If value is zero then skip.
v := c.bitmap[i]
if v == 0 {
continue
}
// Find the highest set bit.
for j := uint32(63); j >= 0; j-- {
if v&(1<<j) != 0 {
return uint32(i)*64 + j
}
}
}
return 0
}
// convertToArray converts the values in the bitmap to array values.
func (c *container) convertToArray() {
c.array = make([]uint32, 0, c.n)
for i, bitmap := range c.bitmap {
for bitmap != 0 {
t := bitmap & -bitmap
c.array = append(c.array, uint32((i*64 + int(popcount(t-1)))))
bitmap ^= t
}
}
c.bitmap = nil
c.mapped = false
}
// convertToBitmap converts the values in array to bitmap values.
func (c *container) convertToBitmap() {
c.bitmap = make([]uint64, bitmapN)
for _, v := range c.array {
c.bitmap[int(v)/64] |= (uint64(1) << uint(v%64))
}
c.array = nil
c.mapped = false
}
// clone returns a copy of c.
func (c *container) clone() *container {
other := &container{n: c.n}
if c.array != nil {
other.array = make([]uint32, len(c.array))
copy(other.array, c.array)
}
if c.bitmap != nil {
other.bitmap = make([]uint64, len(c.bitmap))
copy(other.bitmap, c.bitmap)
}
return other
}
// WriteTo writes c to w.
func (c *container) WriteTo(w io.Writer) (n int64, err error) {
if c.isArray() {
return c.arrayWriteTo(w)
}
return c.bitmapWriteTo(w)
}
func (c *container) arrayWriteTo(w io.Writer) (n int64, err error) {
if len(c.array) == 0 {
return 0, nil
}
// Verify all elements are valid.
// TODO: instead of commenting this out, we need to make it a configuration option
//for _, v := range c.array {
// assert(lowbits(uint64(v)) == v, "cannot write array value out of range: %d", v)
//}
nn, err := w.Write((*[0xFFFFFFF]byte)(unsafe.Pointer(&c.array[0]))[:4*c.n])
return int64(nn), err
}
func (c *container) bitmapWriteTo(w io.Writer) (n int64, err error) {
nn, err := w.Write((*[0xFFFFFFF]byte)(unsafe.Pointer(&c.bitmap[0]))[:(8 * bitmapN)])
return int64(nn), err
}
// size returns the encoded size of the container, in bytes.
func (c *container) size() int {
if c.isArray() {
return len(c.array) * 4
}
return len(c.bitmap) * 8
}
// info returns the current stats about the container.
func (c *container) info() ContainerInfo {
info := ContainerInfo{N: c.n}
if c.isArray() {
info.Type = "array"
info.Alloc = len(c.array) * 4
} else {
info.Type = "bitmap"
info.Alloc = len(c.bitmap) * 8
}
if c.mapped {
if c.isArray() {
info.Pointer = unsafe.Pointer(&c.array[0])
} else {
info.Pointer = unsafe.Pointer(&c.bitmap[0])
}
}
return info
}
// check performs a consistency check on the container.
func (c *container) check() error {
var a ErrorList
if c.n <= ArrayMaxSize {
if len(c.array) != c.n {
a.Append(fmt.Errorf("array count mismatch: count=%d, n=%d", len(c.array), c.n))
}
} else {
if n := c.bitmapCountRange(0, uint32(len(c.bitmap)*64)); n != c.n {
a.Append(fmt.Errorf("bitmap count mismatch: count=%d, n=%d", n, c.n))
}
}
if a == nil {
return nil
}
return a
}
// ContainerInfo represents a point-in-time snapshot of container stats.
type ContainerInfo struct {
Key uint64 // container key
Type string // container type (array or bitmap)
N int // number of bits
Alloc int // memory used
Pointer unsafe.Pointer // offset within the mmap
}
func intersectionCount(a, b *container) uint64 {
if a.isArray() {
if b.isArray() {
return intersectionCountArrayArray(a, b)
} else {
return intersectionCountArrayBitmap(a, b)
}
} else {
if b.isArray() {
return intersectionCountArrayBitmap(b, a)
} else {
return intersectionCountBitmapBitmap(a, b)
}
}
}
func intersectionCountArrayArray(a, b *container) (n uint64) {
na, nb := len(a.array), len(b.array)
for i, j := 0, 0; i < na && j < nb; {
va, vb := a.array[i], b.array[j]
if va < vb {
i++
} else if va > vb {
j++
} else {
n++
i, j = i+1, j+1
}
}
return n
}
func intersectionCountArrayBitmap(a, b *container) (n uint64) {
// Copy array header so we can shrink it.
array := a.array
if len(array) == 0 {
return 0
}
// Iterate over bitmap and find matching bits.
for i, bn := uint32(0), uint32(len(b.bitmap)); i < bn; i++ {
v := b.bitmap[i]
// Ignore if bytes are empty or array is done.
if v == 0 {
continue
}
// Check each bit.
for j := uint32(0); j < 64; j++ {
if v&(1<<j) == 0 {
continue
}
// Search array until match.
bv := (i * 64) + j
for {
if len(array) == 0 {
return n
} else if array[0] < bv {
array = array[1:]
} else if array[0] == bv {
n++
break
} else {
break
}
}
}
}
return n
}
func intersectionCountBitmapBitmap(a, b *container) (n uint64) {
return popcntAndSlice(a.bitmap, b.bitmap)
}
func intersect(a, b *container) *container {
if a.isArray() {
if b.isArray() {
return intersectArrayArray(a, b)
} else {
return intersectArrayBitmap(a, b)
}
} else {
if b.isArray() {
return intersectArrayBitmap(b, a)
} else {
return intersectBitmapBitmap(a, b)
}
}
}
func intersectArrayArray(a, b *container) *container {
output := &container{}
na, nb := len(a.array), len(b.array)
for i, j := 0, 0; i < na && j < nb; {
va, vb := a.array[i], b.array[j]
if va < vb {
i++
} else if va > vb {
j++
} else {
output.array = append(output.array, va)
output.n++
i, j = i+1, j+1
}
}
return output
}
func intersectArrayBitmap(a, b *container) *container {
output := &container{}
itr := newBufIterator(newBitmapIterator(b.bitmap))
for i := 0; i < len(a.array); {
va := a.array[i]
vb, eof := itr.next()
if eof {
break
}
if va < vb {
i++
itr.unread()
} else if va > vb {
// nop
} else {
output.add(va)
i++
}
}
return output
}
func intersectBitmapBitmap(a, b *container) *container {
output := &container{}
itr0 := newBufIterator(newBitmapIterator(a.bitmap))
itr1 := newBufIterator(newBitmapIterator(b.bitmap))
for {
va, eof := itr0.next()
if eof {
break
}
vb, eof := itr1.next()
if eof {
break
}
if va < vb {
itr1.unread()
} else if va > vb {
itr0.unread()
} else {
output.add(va)
}
}
return output
}
func union(a, b *container) *container {
if a.isArray() {
if b.isArray() {
return unionArrayArray(a, b)
} else {
return unionArrayBitmap(a, b)
}
} else {
if b.isArray() {
return unionArrayBitmap(b, a)
} else {
return unionBitmapBitmap(a, b)
}
}
}
func unionArrayArray(a, b *container) *container {
output := &container{}
na, nb := len(a.array), len(b.array)
for i, j := 0, 0; ; {
if i >= na && j >= nb {
break
} else if i < na && j >= nb {
output.add(a.array[i])
i++
continue
} else if i >= na && j < nb {
output.add(b.array[j])
j++
continue
}
va, vb := a.array[i], b.array[j]
if va < vb {
output.add(va)
i++
} else if va > vb {
output.add(vb)
j++
} else {
output.add(va)
i, j = i+1, j+1
}
}
return output
}
func unionArrayBitmap(a, b *container) *container {
output := &container{}
itr := newBufIterator(newBitmapIterator(b.bitmap))
for i := 0; ; {
vb, eof := itr.next()
if i >= len(a.array) && eof {
break
} else if i >= len(a.array) {
output.add(vb)
continue
} else if eof {
output.add(a.array[i])
i++
continue
}
va := a.array[i]
if va < vb {
output.add(va)
i++
itr.unread()
} else if va > vb {
output.add(vb)
} else {
output.add(va)
i++
}
}
return output
}
func unionBitmapBitmap(a, b *container) *container {
output := &container{
bitmap: make([]uint64, bitmapN),
}
for i := 0; i < bitmapN; i++ {
v := a.bitmap[i] | b.bitmap[i]
output.bitmap[i] = v
output.n += int(popcnt(v))
}
return output
}
func difference(a, b *container) *container {
if a.isArray() {
if b.isArray() {
return differenceArrayArray(a, b)
} else {
return differenceArrayBitmap(a, b)
}
} else {
if b.isArray() {
return differenceBitmapArray(a, b)
} else {
return differenceBitmapBitmap(a, b)
}
}
}
func differenceArrayArray(a, b *container) *container {
output := &container{}
na, nb := len(a.array), len(b.array)
for i, j := 0, 0; i < na; {
va := a.array[i]
if j >= nb {
output.add(va)
i++
continue
}
vb := b.array[j]
if va < vb {
output.add(va)
i++
} else if va > vb {
j++
} else {
i, j = i+1, j+1
}
}
return output
}
func differenceArrayBitmap(a, b *container) *container {
output := &container{}
itr := newBufIterator(newBitmapIterator(b.bitmap))
for i := 0; i < len(a.array); {
va := a.array[i]
vb, eof := itr.next()
if eof {
output.add(va)
i++
continue
}
if va < vb {
output.add(va)
i++
itr.unread()
} else if va > vb {
// nop
} else {
i++
}
}
return output
}
func differenceBitmapArray(a, b *container) *container {
output := &container{}
itr := newBufIterator(newBitmapIterator(a.bitmap))
array := b.array
for {
va, eof := itr.next()
if eof {
break
}
if len(array) == 0 {
output.add(va)
continue
}
vb := array[0]
if va < vb {
output.add(va)
} else if va > vb {
array = array[1:]
itr.unread()
} else {
array = array[1:]
}
}
return output
}
func differenceBitmapBitmap(a, b *container) *container {
output := &container{}
itr0 := newBufIterator(newBitmapIterator(a.bitmap))
itr1 := newBufIterator(newBitmapIterator(b.bitmap))
for {
v0, eof0 := itr0.next()
v1, eof1 := itr1.next()
if eof0 {
break
} else if eof1 {
output.add(v0)
} else if v0 < v1 {
output.add(v0)
itr1.unread()
} else if v0 > v1 {
itr0.unread()
}
}
return output
}
// opType represents a type of operation.
type opType uint8
const (
opTypeAdd = opType(0)
opTypeRemove = opType(1)
)
// op represents an operation on the bitmap.
type op struct {
typ opType
value uint64
}
// apply executes the operation against a bitmap.
func (op *op) apply(b *Bitmap) bool {
switch op.typ {
case opTypeAdd:
return b.add(op.value)
case opTypeRemove:
return b.remove(op.value)
default:
panic(fmt.Sprintf("invalid op type: %d", op.typ))
}
return false
}
// WriteTo writes op to the w.
func (op *op) WriteTo(w io.Writer) (n int64, err error) {
buf := make([]byte, op.size())
// Write type and value.
buf[0] = byte(op.typ)
binary.LittleEndian.PutUint64(buf[1:9], op.value)
// Add checksum at the end.
h := fnv.New32a()
h.Write(buf[0:9])
binary.LittleEndian.PutUint32(buf[9:13], h.Sum32())
// Write to writer.
nn, err := w.Write(buf)
return int64(nn), err
}
// UnmarshalBinary decodes data into an op.
func (op *op) UnmarshalBinary(data []byte) error {
if len(data) < op.size() {
return fmt.Errorf("op data out of bounds: len=%d", len(data))
}
// Verify checksum.
h := fnv.New32a()
h.Write(data[0:9])
if chk := binary.LittleEndian.Uint32(data[9:13]); chk != h.Sum32() {
return fmt.Errorf("checksum mismatch: exp=%08x, got=%08x", h.Sum32(), chk)
}
// Read type and value.
op.typ = opType(data[0])
op.value = binary.LittleEndian.Uint64(data[1:9])
return nil
}
// size returns the encoded size of the op, in bytes.
func (*op) size() int { return 1 + 8 + 4 }
func highbits(v uint64) uint64 { return uint64(v >> 16) }
func lowbits(v uint64) uint32 { return uint32(v & 0xFFFF) }
// search32 returns the index of v in a.
func search32(a []uint32, value uint32) int {
// Optimize for elements and the last element.
n := len(a)
if n == 0 {
return -1
} else if a[n-1] == value {
return n - 1
}
// Otherwise perform binary search for exact match.
lo, hi := 0, n-1
for lo+16 <= hi {
i := int(uint((lo + hi)) >> 1)
v := a[i]
if v < value {
lo = i + 1
} else if v > value {
hi = i - 1
} else {
return i
}
}
// If an exact match isn't found then return a negative index.
for ; lo <= hi; lo++ {
v := a[lo]
if v == value {
return lo
} else if v > value {
break
}
}
return -(lo + 1)
}
// search64 returns the index of v in a.
func search64(a []uint64, value uint64) int {
// Optimize for elements and the last element.
n := len(a)
if n == 0 {
return -1
} else if a[n-1] == value {
return n - 1
}
// Otherwise perform binary search for exact match.
lo, hi := 0, n-1
for lo+16 <= hi {
i := int(uint((lo + hi)) >> 1)
v := a[i]
if v < value {
lo = i + 1
} else if v > value {
hi = i - 1
} else {
return i
}
}
// If an exact match isn't found then return a negative index.
for ; lo <= hi; lo++ {
v := a[lo]
if v == value {
return lo
} else if v > value {
break
}
}
return -(lo + 1)
}
// trailingZeroN returns the number of trailing zeros in v.
// v must be greater than zero.
func trailingZeroN(v uint64) int {
n := int64(63)
if y := v << 32; y != 0 {
n, v = n-32, y
}
if y := v << 16; y != 0 {
n, v = n-16, y
}
if y := v << 8; y != 0 {
n, v = n-8, y
}
if y := v << 4; y != 0 {
n, v = n-4, y
}
if y := v << 2; y != 0 {
n, v = n-2, y
}
return int(n - int64(uint64(v<<1)>>63))
}
// bit population count, taken from
// https://code.google.com/p/go/issues/detail?id=4988#c11
// credit: https://code.google.com/u/arnehormann/
func popcount(x uint64) (n uint64) {
x -= (x >> 1) & 0x5555555555555555
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
x += x >> 4
x &= 0x0f0f0f0f0f0f0f0f
x *= 0x0101010101010101
return x >> 56
}
// bitmapIterator represents an iterator over container bitmap values.
type bitmapIterator struct {
bitmap []uint64
i int
}
func newBitmapIterator(bitmap []uint64) *bitmapIterator {
return &bitmapIterator{
bitmap: bitmap,
i: -1,
}
}
// next returns the next value in the bitmap.
// Returns eof as true if there are no values left in the iterator.
func (itr *bitmapIterator) next() (v uint32, eof bool) {
if itr.i+1 >= len(itr.bitmap)*64 {
return 0, true
}
itr.i++
// Find first non-zero bit in current bitmap, if possible.
hb := int(itr.i / 64)
lb := itr.bitmap[hb] >> (uint(itr.i) % 64)
if lb != 0 {
itr.i = int(itr.i) + trailingZeroN(lb)
return uint32(itr.i), false
}
// Otherwise iterate through remaining bitmaps to find next bit.
for hb++; hb < len(itr.bitmap); hb++ {
if itr.bitmap[hb] != 0 {
itr.i = int(hb*64) + trailingZeroN(itr.bitmap[hb])
return uint32(itr.i), false
}
}
return 0, true
}
// bufBitmapIterator wraps an iterator to provide the ability to unread values.
type bufBitmapIterator struct {
buf struct {
v uint32
eof bool
full bool
}
itr *bitmapIterator
}
// newBufBitmapIterator returns a buffered iterator that wraps a bitmapIterator.
func newBufIterator(itr *bitmapIterator) *bufBitmapIterator {
return &bufBitmapIterator{itr: itr}
}
// next returns the next pair in the bitmap.
// If a value has been buffered then it is returned and the buffer is cleared.
func (itr *bufBitmapIterator) next() (v uint32, eof bool) {
if itr.buf.full {
itr.buf.full = false
return itr.buf.v, itr.buf.eof
}
// Read value onto buffer in case of unread.
itr.buf.v, itr.buf.eof = itr.itr.next()
return itr.buf.v, itr.buf.eof
}
// unread pushes previous pair on to the buffer. Panics if the buffer is already full.
func (itr *bufBitmapIterator) unread() {
if itr.buf.full {
panic("roaring.bufBitmapIterator: buffer full")
}
itr.buf.full = true
}
// ErrorList represents a list of errors.
type ErrorList []error
func (a ErrorList) Error() string {
switch len(a) {
case 0:
return "no errors"
case 1:
return a[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", a[0], len(a)-1)
}
// Append appends an error to the list. If err is an ErrorList then all errors are appended.
func (a *ErrorList) Append(err error) {
switch err := err.(type) {
case ErrorList:
*a = append(*a, err...)
default:
*a = append(*a, err)
}
}
// AppendWithPrefix appends an error to the list and includes a prefix.
func (a *ErrorList) AppendWithPrefix(err error, prefix string) {
switch err := err.(type) {
case ErrorList:
for i := range err {
*a = append(*a, fmt.Errorf("%s%s", prefix, err[i]))
}
default:
*a = append(*a, fmt.Errorf("%s%s", prefix, err))
}
}
// assert panics with a formatted message if condition is false.
func assert(condition bool, format string, a ...interface{}) {
if !condition {
panic(fmt.Sprintf(format, a...))
}
}