Fixed conflicts; Merged with master

This commit is contained in:
Yuce Tekol 2019-05-31 17:16:54 +03:00
commit 08f4ccb29b
No known key found for this signature in database
GPG key ID: CB59E46D2FB90573
17 changed files with 1456 additions and 1973 deletions

View file

@ -452,9 +452,14 @@ func (s *simpleCache) Fetch(id uint64) (*Row, bool) {
return m, ok
}
// Add adds the bitmap to the cache, keyed on the id.
// Add adds the bitmap to the cache, keyed on the id. A nil row means
// deleting the row from the cache.
func (s *simpleCache) Add(id uint64, b *Row) {
s.cache[id] = b
if b != nil {
s.cache[id] = b
} else {
delete(s.cache, id)
}
}
// nopCache represents a no-op Cache implementation.

View file

@ -1,953 +0,0 @@
// This file is a modified redistribution of b (https://github.com/cznic/b),
// which is governed by the following license notice:
//
// Copyright (c) 2014 The b Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the names of the authors nor the names of the
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package b
import (
"io"
"sync"
"github.com/pilosa/pilosa/roaring"
)
const (
// kx must be >= 2
kx = 128 //TODO benchmark tune this number if using custom key/value type(s).
// kd must be >= 1
kd = 128 //TODO benchmark tune this number if using custom key/value type(s).
)
var (
btDPool = sync.Pool{New: func() interface{} { return &d{} }}
btEPool = btEpool{sync.Pool{New: func() interface{} { return &enumerator{} }}}
btTPool = btTpool{sync.Pool{New: func() interface{} { return &tree{} }}}
btXPool = sync.Pool{New: func() interface{} { return &x{} }}
)
type btTpool struct{ sync.Pool }
func (p *btTpool) get(cmp Cmp) *tree {
x := p.Get().(*tree)
x.cmp = cmp
return x
}
type btEpool struct{ sync.Pool }
func (p *btEpool) get(err error, hit bool, i int, k uint64, q *d, t *tree, ver int64) *enumerator {
x := p.Get().(*enumerator)
x.err, x.hit, x.i, x.k, x.q, x.t, x.ver = err, hit, i, k, q, t, ver
return x
}
type (
// Cmp compares a and b. Return value is:
//
// < 0 if a < b
// 0 if a == b
// > 0 if a > b
//
Cmp func(a, b uint64) int64
d struct { // data page
c int
d [2*kd + 1]de
n *d
p *d
}
de struct { // d element
k uint64
v *roaring.Container
}
// enumerator captures the state of enumerating a tree. It is returned
// from the Seek* methods. The enumerator is aware of any mutations
// made to the tree in the process of enumerating it and automatically
// resumes the enumeration at the proper key, if possible.
//
// However, once an enumerator returns io.EOF to signal "no more
// items", it does no more attempt to "resync" on tree mutation(s). In
// other words, io.EOF from an enumerator is "sticky" (idempotent).
enumerator struct {
err error
hit bool
i int
k uint64
q *d
t *tree
ver int64
}
// tree is a B+tree.
tree struct {
c int
cmp Cmp
first *d
last *d
r interface{}
ver int64
}
xe struct { // x element
ch interface{}
k uint64
}
x struct { // index page
c int
x [2*kx + 2]xe
}
)
var ( // R/O zero values
zd d
zde de
ze enumerator
zk uint64
zt tree
zx x
zxe xe
)
func clr(q interface{}) {
switch x := q.(type) {
case *x:
for i := 0; i <= x.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn
clr(x.x[i].ch)
}
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
}
// -------------------------------------------------------------------------- x
func newX(ch0 interface{}) *x {
r := btXPool.Get().(*x)
r.x[0].ch = ch0
return r
}
func (q *x) extract(i int) {
q.c--
if i < q.c {
copy(q.x[i:], q.x[i+1:q.c+1])
q.x[q.c].ch = q.x[q.c+1].ch
q.x[q.c].k = zk // GC
q.x[q.c+1] = zxe // GC
}
}
func (q *x) insert(i int, k uint64, ch interface{}) *x {
c := q.c
if i < c {
q.x[c+1].ch = q.x[c].ch
copy(q.x[i+2:], q.x[i+1:c])
q.x[i+1].k = q.x[i].k
}
c++
q.c = c
q.x[i].k = k
q.x[i+1].ch = ch
return q
}
func (q *x) siblings(i int) (l, r *d) {
if i >= 0 {
if i > 0 {
l = q.x[i-1].ch.(*d)
}
if i < q.c {
r = q.x[i+1].ch.(*d)
}
}
return l, r
}
// -------------------------------------------------------------------------- d
func (l *d) mvL(r *d, c int) {
copy(l.d[l.c:], r.d[:c])
copy(r.d[:], r.d[c:r.c])
// Zero out the de's here to prevent reading bad data
// and to avoid creating non-collectible (GC) references.
for i := 1; i < c; i++ {
r.d[r.c-i] = zde
}
l.c += c
r.c -= c
}
func (l *d) mvR(r *d, c int) {
copy(r.d[c:], r.d[:r.c])
copy(r.d[:c], l.d[l.c-c:])
// Zero out the de's here to prevent reading bad data
// and to avoid creating non-collectible (GC) references.
for i := 1; i < c; i++ {
l.d[l.c-c+i] = zde
}
r.c += c
l.c -= c
}
// ----------------------------------------------------------------------- Tree
// treeNew returns a newly created, empty Tree. The compare function is used
// for key collation.
func treeNew(cmp Cmp) *tree {
return btTPool.get(cmp)
}
// Clear removes all K/V pairs from the tree.
func (t *tree) Clear() {
if t.r == nil {
return
}
clr(t.r)
t.c, t.first, t.last, t.r = 0, nil, nil, nil
t.ver++
}
// Close performs Clear and recycles t to a pool for possible later reuse. No
// references to t should exist or such references must not be used afterwards.
func (t *tree) Close() {
t.Clear()
*t = zt
btTPool.Put(t)
}
func (t *tree) cat(p *x, q, r *d, pi int) {
t.ver++
q.mvL(r, r.c)
if r.n != nil {
r.n.p = q
} else {
t.last = q
}
q.n = r.n
*r = zd
btDPool.Put(r)
if p.c > 1 {
p.extract(pi)
p.x[pi].ch = q
return
}
switch x := t.r.(type) {
case *x:
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
t.r = q
}
func (t *tree) catX(p, q, r *x, pi int) {
t.ver++
q.x[q.c].k = p.x[pi].k
copy(q.x[q.c+1:], r.x[:r.c])
q.c += r.c + 1
q.x[q.c].ch = r.x[r.c].ch
*r = zx
btXPool.Put(r)
if p.c > 1 {
p.c--
pc := p.c
if pi < pc {
p.x[pi].k = p.x[pi+1].k
copy(p.x[pi+1:], p.x[pi+2:pc+1])
p.x[pc].ch = p.x[pc+1].ch
p.x[pc].k = zk // GC
p.x[pc+1].ch = nil // GC
}
return
}
switch x := t.r.(type) {
case *x:
*x = zx
btXPool.Put(x)
case *d:
*x = zd
btDPool.Put(x)
}
t.r = q
}
// Delete removes the k's KV pair, if it exists, in which case Delete returns
// true.
func (t *tree) Delete(k uint64) (ok bool) {
pi := -1
var p *x
q := t.r
if q == nil {
return false
}
for {
var i int
i, ok = t.find(q, k)
if ok {
switch x := q.(type) {
case *x:
if x.c < kx && q != t.r {
x, i = t.underflowX(p, x, pi, i)
}
pi = i + 1
p = x
q = x.x[pi].ch
continue
case *d:
t.extract(x, i)
if x.c >= kd {
return true
}
if q != t.r {
t.underflow(p, x, pi)
} else if t.c == 0 {
t.Clear()
}
return true
}
}
switch x := q.(type) {
case *x:
if x.c < kx && q != t.r {
x, i = t.underflowX(p, x, pi, i)
}
pi = i
p = x
q = x.x[i].ch
case *d:
return false
}
}
}
func (t *tree) extract(q *d, i int) { // (r *container) {
t.ver++
//r = q.d[i].v // prepared for Extract
q.c--
if i < q.c {
copy(q.d[i:], q.d[i+1:q.c+1])
}
q.d[q.c] = zde // GC
t.c--
}
func (t *tree) find(q interface{}, k uint64) (i int, ok bool) {
var mk uint64
l := 0
switch x := q.(type) {
case *x:
h := x.c - 1
for l <= h {
m := (l + h) >> 1
mk = x.x[m].k
switch cmp := t.cmp(k, mk); {
case cmp > 0:
l = m + 1
case cmp == 0:
return m, true
default:
h = m - 1
}
}
case *d:
h := x.c - 1
for l <= h {
m := (l + h) >> 1
mk = x.d[m].k
switch cmp := t.cmp(k, mk); {
case cmp > 0:
l = m + 1
case cmp == 0:
return m, true
default:
h = m - 1
}
}
}
return l, false
}
// First returns the first item of the tree in the key collating order, or
// (zero-value, zero-value) if the tree is empty.
func (t *tree) First() (k uint64, v *roaring.Container) {
if q := t.first; q != nil {
q := &q.d[0]
k, v = q.k, q.v
}
return k, v
}
// Get returns the value associated with k and true if it exists. Otherwise Get
// returns (zero-value, false).
func (t *tree) Get(k uint64) (v *roaring.Container, ok bool) {
q := t.r
if q == nil {
return
}
for {
var i int
if i, ok = t.find(q, k); ok {
switch x := q.(type) {
case *x:
q = x.x[i+1].ch
continue
case *d:
return x.d[i].v, true
}
}
switch x := q.(type) {
case *x:
q = x.x[i].ch
default:
return
}
}
}
func (t *tree) insert(q *d, i int, k uint64, v *roaring.Container) *d {
t.ver++
c := q.c
if i < c {
copy(q.d[i+1:], q.d[i:c])
}
c++
q.c = c
q.d[i].k, q.d[i].v = k, v
t.c++
return q
}
// Last returns the last item of the tree in the key collating order, or
// (zero-value, zero-value) if the tree is empty.
func (t *tree) Last() (k uint64, v *roaring.Container) {
if q := t.last; q != nil {
q := &q.d[q.c-1]
k, v = q.k, q.v
}
return k, v
}
// Len returns the number of items in the tree.
func (t *tree) Len() int {
return t.c
}
func (t *tree) overflow(p *x, q *d, pi, i int, k uint64, v *roaring.Container) {
t.ver++
l, r := p.siblings(pi)
// s is the number of items to shift out of the full data container to
// allow for the new data item. This logic shifts by half the available
// space plus one. In the case where the new item is to be inserted within
// the calculated shift space, then s is reduced to include only the
// data items up to the index of the new data item.
if l != nil && l.c < 2*kd && i != 0 {
s := (2*kd-l.c)/2 + 1 // half plus one
//s := 2*kd - l.c // all available
if i < s {
s = i
}
l.mvL(q, s)
t.insert(q, i-s, k, v)
p.x[pi-1].k = q.d[0].k
return
}
if r != nil && r.c < 2*kd {
if i < 2*kd {
s := (2*kd-r.c)/2 + 1 // half plus one
//s := 2*kd - r.c // all available
if 2*kd-i < s {
s = 2*kd - i
}
q.mvR(r, s)
t.insert(q, i, k, v)
p.x[pi].k = r.d[0].k
return
}
t.insert(r, 0, k, v)
p.x[pi].k = k
return
}
t.split(p, q, pi, i, k, v)
}
// Seek returns an Enumerator positioned on an item such that k >= item's key.
// ok reports if k == item.key The Enumerator's position is possibly after the
// last item in the tree.
func (t *tree) Seek(k uint64) (e *enumerator, ok bool) {
q := t.r
if q == nil {
e = btEPool.get(nil, false, 0, k, nil, t, t.ver)
return
}
for {
var i int
if i, ok = t.find(q, k); ok {
switch x := q.(type) {
case *x:
q = x.x[i+1].ch
continue
case *d:
return btEPool.get(nil, ok, i, k, x, t, t.ver), true
}
}
switch x := q.(type) {
case *x:
q = x.x[i].ch
case *d:
return btEPool.get(nil, ok, i, k, x, t, t.ver), false
}
}
}
// SeekFirst returns an enumerator positioned on the first KV pair in the tree,
// if any. For an empty tree, err == io.EOF is returned and e will be nil.
func (t *tree) SeekFirst() (e *enumerator, err error) {
q := t.first
if q == nil {
return nil, io.EOF
}
return btEPool.get(nil, true, 0, q.d[0].k, q, t, t.ver), nil
}
// SeekLast returns an enumerator positioned on the last KV pair in the tree,
// if any. For an empty tree, err == io.EOF is returned and e will be nil.
func (t *tree) SeekLast() (e *enumerator, err error) {
q := t.last
if q == nil {
return nil, io.EOF
}
return btEPool.get(nil, true, q.c-1, q.d[q.c-1].k, q, t, t.ver), nil
}
// Set sets the value associated with k.
func (t *tree) Set(k uint64, v *roaring.Container) {
//dbg("--- PRE Set(%v, %v)\n%s", k, v, t.dump())
//defer func() {
// dbg("--- POST\n%s\n====\n", t.dump())
//}()
pi := -1
var p *x
q := t.r
if q == nil {
z := t.insert(btDPool.Get().(*d), 0, k, v)
t.r, t.first, t.last = z, z, z
return
}
for {
i, ok := t.find(q, k)
if ok {
switch x := q.(type) {
case *x:
i++
if x.c > 2*kx {
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
q = x.x[i].ch
continue
case *d:
x.d[i].v = v
}
return
}
switch x := q.(type) {
case *x:
if x.c > 2*kx {
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
q = x.x[i].ch
case *d:
switch {
case x.c < 2*kd:
t.insert(x, i, k, v)
default:
t.overflow(p, x, pi, i, k, v)
}
return
}
}
}
// Put combines Get and Set in a more efficient way where the tree is walked
// only once. The upd(ater) receives (old-value, true) if a KV pair for k
// exists or (zero-value, false) otherwise. It can then return a (new-value,
// true) to create or overwrite the existing value in the KV pair, or
// (whatever, false) if it decides not to create or not to update the value of
// the KV pair.
//
// tree.Set(k, v) call conceptually equals calling
//
// tree.Put(k, func(uint64, bool){ return v, true })
//
// modulo the differing return values.
func (t *tree) Put(k uint64, upd func(oldV *roaring.Container, exists bool) (newV *roaring.Container, write bool)) (oldV *roaring.Container, written bool) {
pi := -1
var p *x
q := t.r
var newV *roaring.Container
if q == nil {
// new KV pair in empty tree
newV, written = upd(newV, false)
if !written {
return
}
z := t.insert(btDPool.Get().(*d), 0, k, newV)
t.r, t.first, t.last = z, z, z
return
}
for {
i, ok := t.find(q, k)
if ok {
switch x := q.(type) {
case *x:
i++
if x.c > 2*kx {
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
q = x.x[i].ch
continue
case *d:
oldV = x.d[i].v
newV, written = upd(oldV, true)
if !written {
return
}
x.d[i].v = newV
}
return
}
switch x := q.(type) {
case *x:
if x.c > 2*kx {
x, i = t.splitX(p, x, pi, i)
}
pi = i
p = x
q = x.x[i].ch
case *d: // new KV pair
newV, written = upd(newV, false)
if !written {
return
}
switch {
case x.c < 2*kd:
t.insert(x, i, k, newV)
default:
t.overflow(p, x, pi, i, k, newV)
}
return
}
}
}
func (t *tree) split(p *x, q *d, pi, i int, k uint64, v *roaring.Container) {
t.ver++
r := btDPool.Get().(*d)
if q.n != nil {
r.n = q.n
r.n.p = r
} else {
t.last = r
}
q.n = r
r.p = q
copy(r.d[:], q.d[kd:2*kd])
for i := range q.d[kd:] {
q.d[kd+i] = zde
}
q.c = kd
r.c = kd
var done bool
if i > kd {
done = true
t.insert(r, i-kd, k, v)
}
if pi >= 0 {
p.insert(pi, r.d[0].k, r)
} else {
t.r = newX(q).insert(0, r.d[0].k, r)
}
if done {
return
}
t.insert(q, i, k, v)
}
func (t *tree) splitX(p *x, q *x, pi int, i int) (*x, int) {
t.ver++
r := btXPool.Get().(*x)
copy(r.x[:], q.x[kx+1:])
q.c = kx
r.c = kx
if pi >= 0 {
p.insert(pi, q.x[kx].k, r)
} else {
t.r = newX(q).insert(0, q.x[kx].k, r)
}
q.x[kx].k = zk
for i := range q.x[kx+1:] {
q.x[kx+i+1] = zxe
}
if i > kx {
q = r
i -= kx + 1
}
return q, i
}
func (t *tree) underflow(p *x, q *d, pi int) {
t.ver++
l, r := p.siblings(pi)
if l != nil && l.c+q.c >= 2*kd {
l.mvR(q, 1)
p.x[pi-1].k = q.d[0].k
return
}
if r != nil && q.c+r.c >= 2*kd {
q.mvL(r, 1)
p.x[pi].k = r.d[0].k
r.d[r.c] = zde // GC
return
}
if l != nil {
t.cat(p, l, q, pi-1)
return
}
t.cat(p, q, r, pi)
}
func (t *tree) underflowX(p *x, q *x, pi int, i int) (*x, int) {
t.ver++
var l, r *x
if pi >= 0 {
if pi > 0 {
l = p.x[pi-1].ch.(*x)
}
if pi < p.c {
r = p.x[pi+1].ch.(*x)
}
}
if l != nil && l.c > kx {
q.x[q.c+1].ch = q.x[q.c].ch
copy(q.x[1:], q.x[:q.c])
q.x[0].ch = l.x[l.c].ch
q.x[0].k = p.x[pi-1].k
q.c++
i++
l.c--
p.x[pi-1].k = l.x[l.c].k
return q, i
}
if r != nil && r.c > kx {
q.x[q.c].k = p.x[pi].k
q.c++
q.x[q.c].ch = r.x[0].ch
p.x[pi].k = r.x[0].k
copy(r.x[:], r.x[1:r.c])
r.c--
rc := r.c
r.x[rc].ch = r.x[rc+1].ch
r.x[rc].k = zk
r.x[rc+1].ch = nil
return q, i
}
if l != nil {
i += l.c + 1
t.catX(p, l, q, pi-1)
q = l
return q, i
}
t.catX(p, q, r, pi)
return q, i
}
// ----------------------------------------------------------------- Enumerator
// Close recycles e to a pool for possible later reuse. No references to e
// should exist or such references must not be used afterwards.
func (e *enumerator) Close() {
*e = ze
btEPool.Put(e)
}
// Next returns the currently enumerated item, if it exists and moves to the
// next item in the key collation order. If there is no item to return, err ==
// io.EOF is returned.
func (e *enumerator) Next() (k uint64, v *roaring.Container, err error) {
if err = e.err; err != nil {
return 0, nil, err
}
if e.ver != e.t.ver {
f, _ := e.t.Seek(e.k)
*e = *f
f.Close()
}
if e.q == nil {
e.err, err = io.EOF, io.EOF
return 0, nil, err
}
if e.i >= e.q.c {
if err = e.next(); err != nil {
return 0, nil, err
}
}
i := e.q.d[e.i]
k, v = i.k, i.v
e.k, e.hit = k, true
_ = e.next()
return k, v, nil
}
func (e *enumerator) next() error {
if e.q == nil {
e.err = io.EOF
return io.EOF
}
switch {
case e.i < e.q.c-1:
e.i++
default:
if e.q, e.i = e.q.n, 0; e.q == nil {
e.err = io.EOF
}
}
return e.err
}
// Prev returns the currently enumerated item, if it exists and moves to the
// previous item in the key collation order. If there is no item to return, err
// == io.EOF is returned.
func (e *enumerator) Prev() (k uint64, v *roaring.Container, err error) {
if err = e.err; err != nil {
return 0, nil, err
}
if e.ver != e.t.ver {
f, _ := e.t.Seek(e.k)
*e = *f
f.Close()
}
if e.q == nil {
e.err, err = io.EOF, io.EOF
return 0, nil, err
}
if !e.hit {
// move to previous because Seek overshoots if there's no hit
if err = e.prev(); err != nil {
return 0, nil, err
}
}
if e.i >= e.q.c {
if err = e.prev(); err != nil {
return 0, nil, err
}
}
i := e.q.d[e.i]
k, v = i.k, i.v
e.k, e.hit = k, true
_ = e.prev()
return k, v, err
}
func (e *enumerator) prev() error {
if e.q == nil {
e.err = io.EOF
return io.EOF
}
switch {
case e.i > 0:
e.i--
default:
if e.q = e.q.p; e.q == nil {
e.err = io.EOF
break
}
e.i = e.q.c - 1
}
return e.err
}

View file

@ -1,219 +0,0 @@
// Copyright (c) 2018 Pilosa Corp. All rights reserved.
//
// This file is part of Pilosa Enterprise Edition.
//
// Pilosa Enterprise Edition is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pilosa Enterprise Edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pilosa Enterprise Edition. If not, see <http://www.gnu.org/licenses/>.
package b
import (
"io"
"github.com/pilosa/pilosa/roaring"
)
func cmp(a, b uint64) int64 {
return int64(a - b)
}
type bTreeContainers struct {
tree *tree
lastKey uint64
lastContainer *roaring.Container
}
func newBTreeContainers() *bTreeContainers {
return &bTreeContainers{
tree: treeNew(cmp),
}
}
func NewBTreeBitmap(a ...uint64) *roaring.Bitmap {
b := &roaring.Bitmap{
Containers: newBTreeContainers(),
}
// TODO: there's no way to report an error here
_, _ = b.Add(a...)
return b
}
func (btc *bTreeContainers) Get(key uint64) *roaring.Container {
// Check the last* cache for same container.
if key == btc.lastKey && btc.lastContainer != nil {
return btc.lastContainer
}
var c *roaring.Container
el, ok := btc.tree.Get(key)
if ok {
c = el
btc.lastKey = key
btc.lastContainer = c
}
return c
}
func (btc *bTreeContainers) Put(key uint64, c *roaring.Container) {
// If a mapped container is added to the tree, reset the
// lastContainer cache so that the cache is not pointing
// at a read-only mmap.
if c.Mapped() {
btc.lastContainer = nil
}
btc.tree.Set(key, c)
}
func (u updater) update(oldV *roaring.Container, exists bool) (*roaring.Container, bool) {
// update the existing container
if exists {
oldV.Update(u.containerType, u.n, u.mapped)
return oldV, false
}
cont := roaring.NewContainer()
cont.Update(u.containerType, u.n, u.mapped)
return cont, true
}
// this struct is added to prevent the closure locals from being escaped out to the heap
type updater struct {
key uint64
n int32
containerType byte
mapped bool
}
func (btc *bTreeContainers) PutContainerValues(key uint64, typ byte, n int, mapped bool) {
a := updater{key, int32(n), typ, mapped}
btc.tree.Put(key, a.update)
}
func (btc *bTreeContainers) Remove(key uint64) {
btc.tree.Delete(key)
}
func (btc *bTreeContainers) GetOrCreate(key uint64) *roaring.Container {
// Check the last* cache for same container.
if key == btc.lastKey && btc.lastContainer != nil {
return btc.lastContainer
}
btc.lastKey = key
v, ok := btc.tree.Get(key)
if !ok {
cont := roaring.NewContainerArray(nil)
btc.tree.Set(key, cont)
btc.lastContainer = cont
return cont
}
btc.lastContainer = v
return btc.lastContainer
}
func (btc *bTreeContainers) Count() (n uint64) {
e, _ := btc.tree.Seek(0)
_, c, err := e.Next()
for err != io.EOF {
n += uint64(c.N())
_, c, err = e.Next()
}
return n
}
func (btc *bTreeContainers) Clone() roaring.Containers {
nbtc := newBTreeContainers()
itr, err := btc.tree.SeekFirst()
if err == io.EOF {
return nbtc
}
for {
k, v, err := itr.Next()
if err == io.EOF {
break
}
nbtc.tree.Set(k, v.Clone())
}
return nbtc
}
func (btc *bTreeContainers) First() (key uint64, c *roaring.Container) {
if btc.tree.Len() == 0 {
return 0, nil
}
return btc.tree.First()
}
func (btc *bTreeContainers) Last() (key uint64, c *roaring.Container) {
if btc.tree.Len() == 0 {
return 0, nil
}
k, v := btc.tree.Last()
return k, v
}
func (btc *bTreeContainers) Size() int {
return btc.tree.Len()
}
func (btc *bTreeContainers) Reset() {
btc.tree = treeNew(cmp)
btc.lastKey = 0
btc.lastContainer = nil
}
func (btc *bTreeContainers) Iterator(key uint64) (citer roaring.ContainerIterator, found bool) {
e, ok := btc.tree.Seek(key)
if ok {
found = true
}
return &btcIterator{
e: e,
}, found
}
func (btc *bTreeContainers) Repair() {
e, _ := btc.tree.Seek(0)
_, c, err := e.Next()
for err != io.EOF {
c.Repair()
_, c, err = e.Next()
}
}
type btcIterator struct {
e *enumerator
key uint64
val *roaring.Container
}
func (i *btcIterator) Next() bool {
k, v, err := i.e.Next()
if err == io.EOF {
return false
}
i.key = k
i.val = v
return true
}
func (i *btcIterator) Value() (uint64, *roaring.Container) {
if i.val == nil {
return 0, nil
}
return i.key, i.val
}

View file

@ -21,13 +21,3 @@
// "ENTERPRISE=1 make install". These features were dual-licensed separately
// from Pilosa community edition under the AGPL and Pilosa's commercial license.
package enterprise
import (
"github.com/pilosa/pilosa/enterprise/b"
"github.com/pilosa/pilosa/roaring"
)
func init() { // nolint: gochecknoinits
// Replace Bitmap constructor with B+Tree implementation
roaring.NewFileBitmap = b.NewBTreeBitmap
}

View file

@ -426,19 +426,18 @@ func (f *fragment) unprotectedRow(rowID uint64) *Row {
func (f *fragment) rowFromStorage(rowID uint64) *Row {
// Only use a subset of the containers.
// NOTE: The start & end ranges must be divisible by container width.
//
// Note that OffsetRange now returns a new bitmap which uses frozen
// containers which will use copy-on-write semantics. The actual bitmap
// and Containers object are new and not shared, but the containers are
// shared.
data := f.storage.OffsetRange(f.shard*ShardWidth, rowID*ShardWidth, (rowID+1)*ShardWidth)
// Reference bitmap subrange in storage. We Clone() data because otherwise
// row will contain pointers to containers in storage. This causes
// unexpected results when we cache the row and try to use it later.
// Basically, since we return the Row and release the fragment lock, the
// underlying fragment storage could be changed or snapshotted and thrown
// out at any point.
row := &Row{
segments: []rowSegment{{
data: *data.Clone(),
data: data,
shard: f.shard,
writable: false, // this Row will probably be cached and shared, so it must be read only.
writable: true,
}},
}
row.invalidateCount()
@ -509,12 +508,15 @@ func (f *fragment) unprotectedSetBit(rowID, columnID uint64) (changed bool, err
return false, errors.Wrap(err, "incrementing")
}
// Get the row from row cache or fragment.storage.
row := f.unprotectedRow(rowID)
row.SetBit(columnID)
// Update the cache.
f.cache.Add(rowID, row.Count())
// If we're using a cache, update it. Otherwise skip the
// possibly-expensive count operation.
if f.CacheType != CacheTypeNone {
n := f.storage.CountRange(rowID*ShardWidth, (rowID+1)*ShardWidth)
f.cache.Add(rowID, n)
}
// Drop the rowCache entry; it's wrong, and we don't want to force
// a new copy if no one's reading it.
f.rowCache.Add(rowID, nil)
f.stats.Count("setBit", 1, 0.001)
@ -574,12 +576,15 @@ func (f *fragment) unprotectedClearBit(rowID, columnID uint64) (changed bool, er
return false, errors.Wrap(err, "incrementing")
}
// Get the row from cache or fragment.storage.
row := f.unprotectedRow(rowID)
row.clearBit(columnID)
// Update the cache.
f.cache.Add(rowID, row.Count())
// If we're using a cache, update it. Otherwise skip the
// possibly-expensive count operation.
if f.CacheType != CacheTypeNone {
n := f.storage.CountRange(rowID*ShardWidth, (rowID+1)*ShardWidth)
f.cache.Add(rowID, n)
}
// Drop the rowCache entry; it's wrong, and we don't want to force
// a new copy if no one's reading it.
f.rowCache.Add(rowID, nil)
f.stats.Count("clearBit", 1, 1.0)
@ -1857,9 +1862,7 @@ func (f *fragment) importPositions(set, clear []uint64, rowSet map[uint64]struct
f.cache.BulkAdd(rowID, n)
if smallWrite {
if _, ok := f.rowCache.Fetch(rowID); ok { // we won't update the rowCache if it wasn't already in there.
f.rowCache.Add(rowID, f.rowFromStorage(rowID))
}
f.rowCache.Add(rowID, nil)
}
}

View file

@ -34,6 +34,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/pilosa/pilosa/pql"
"github.com/pilosa/pilosa/roaring"
"github.com/pkg/errors"
)
// Test flags
@ -3292,3 +3293,30 @@ func TestImportMultipleValues(t *testing.T) {
}
}
}
func TestFragmentConcurrentReadWrite(t *testing.T) {
f := mustOpenFragment("i", "f", viewStandard, 0, CacheTypeRanked)
defer f.Clean(t)
eg := &errgroup.Group{}
eg.Go(func() error {
for i := uint64(0); i < 1000; i++ {
_, err := f.setBit(i%4, i)
if err != nil {
return errors.Wrap(err, "setting bit")
}
}
return nil
})
acc := uint64(0)
for i := uint64(0); i < 100; i++ {
r := f.row(i % 4)
acc += r.Count()
}
if err := eg.Wait(); err != nil {
t.Errorf("error from setting a bit: %v", err)
}
t.Logf("%d", acc)
}

View file

@ -570,6 +570,13 @@ func (t *tree) Set(k uint64, v *Container) {
//defer func() {
// dbg("--- POST\n%s\n====\n", t.dump())
//}()
// we don't want to store nil containers; if you try to set a
// container to nil, that's equivalent to not having one at that
// location.
if v == nil {
_ = t.Delete(k)
return
}
pi := -1
var p *x
@ -642,7 +649,9 @@ func (t *tree) Put(k uint64, upd func(oldV *Container, exists bool) (newV *Conta
if !written {
return
}
if newV == nil {
return
}
z := t.insert(btDPool.Get().(*d), 0, k, newV)
t.r, t.first, t.last = z, z, z
return
@ -667,7 +676,11 @@ func (t *tree) Put(k uint64, upd func(oldV *Container, exists bool) (newV *Conta
if !written {
return
}
// delete nil containers rather than storing them.
if newV == nil {
t.Delete(k)
return
}
x.d[i].v = newV
}
return
@ -686,6 +699,10 @@ func (t *tree) Put(k uint64, upd func(oldV *Container, exists bool) (newV *Conta
if !written {
return
}
// nil values don't need to exist, and break iteration later.
if newV == nil {
return
}
switch {
case x.c < 2*kd:
@ -876,6 +893,46 @@ func (e *enumerator) Next() (k uint64, v *Container, err error) {
return k, v, err
}
// Every iterates over a tree.
func (e *enumerator) Every(upd func(oldV *Container, exists bool) (newV *Container, write bool)) error {
if err := e.err; err != nil {
return err
}
if e.ver != e.t.ver {
f, _ := e.t.Seek(e.k)
*e = *f
f.Close()
}
for {
if e.q == nil {
e.err = io.EOF
return e.err
}
if e.i >= e.q.c {
if err := e.next(); err != nil {
e.err = err
return e.err
}
}
i := e.q.d[e.i]
nv, write := upd(i.v, true)
if write {
if nv == nil {
e.t.Delete(e.q.d[e.i].k)
} else {
e.q.d[e.i].v = nv
}
}
// Any error returned would be stashed in e.err, and would come up
// on the next call.
_ = e.next()
}
}
func (e *enumerator) next() error {
if e.q == nil {
e.err = io.EOF

View file

@ -467,13 +467,14 @@ func benchmarkSetRnd(b *testing.B, n int) {
a[i] = rng.Next()
}
b.ResetTimer()
c := getDummyC(1)
for i := 0; i < b.N; i++ {
b.StopTimer()
r := treeNew()
debug.FreeOSMemory()
b.StartTimer()
for _, v := range a {
r.Set(uint64(v), nil)
r.Set(uint64(v), c)
}
b.StopTimer()
r.Close()
@ -504,8 +505,9 @@ func benchmarkGetRnd(b *testing.B, n int) {
for i := range a {
a[i] = rng.Next()
}
c := getDummyC(1)
for _, v := range a {
r.Set(uint64(v), nil)
r.Set(uint64(v), c)
}
debug.FreeOSMemory()
b.ResetTimer()
@ -1392,7 +1394,7 @@ func TestBtreePut(t *testing.T) {
t.Fatal(iTest, g, e)
}
}
return nil, test.write
return getDummyC(99), test.write
})
if test.exists {
if g, e := oldV, getDummyC(test.oldV); g != e {
@ -1427,6 +1429,8 @@ func TestBtreePut(t *testing.T) {
var e *Container
if test.post[i+1] != -1 {
e = getDummyC(test.post[i+1])
} else {
e = getDummyC(99)
}
if g := v; g != e {
t.Fatal(iTest, g, e)
@ -1445,7 +1449,7 @@ func TestBtreeSeek(t *testing.T) {
tr := treeNew()
for i := 0; i < N; i++ {
k := 2*i + 1
tr.Set(uint64(k), nil)
tr.Set(uint64(k), getDummyC(1))
}
for i := 0; i < N; i++ {
k := 2 * i
@ -1476,14 +1480,14 @@ func TestBtreePR4(t *testing.T) {
tr := treeNew()
for i := 0; i < 2*kd+1; i++ {
k := 1000 * i
tr.Set(uint64(k), nil)
tr.Set(uint64(k), getDummyC(1))
}
tr.Delete(1000 * kd)
for i := 0; i < kd; i++ {
tr.Set(uint64(1000*(kd+1)-1-i), nil)
tr.Set(uint64(1000*(kd+1)-1-i), getDummyC(1))
}
k := 1000*(kd+1) - 1 - kd
tr.Set(uint64(k), nil)
tr.Set(uint64(k), getDummyC(1))
if _, ok := tr.Get(uint64(k)); !ok {
t.Fatalf("key lost: %v", k)
}

View file

@ -15,6 +15,7 @@
package roaring
import (
"fmt"
"reflect"
"runtime"
"unsafe"
@ -39,24 +40,84 @@ type Container struct {
pointer *uint16 // the data pointer
len, cap int32 // length and cap
n int32 // number of integers in container
mapped bool // mapped directly to a byte slice when true
typ byte // array, bitmap, or run
flags containerFlags // internal flags
typeID byte // array, bitmap, or run
data [stashedArraySize]uint16 // immediate data for small arrays or runs
}
type containerFlags uint8
const (
flagMapped = containerFlags(1 << iota)
flagFrozen
)
func (c *Container) String() string {
if c == nil {
return "<nil container>"
}
froze := ""
switch c.flags {
case flagFrozen:
froze = "frozen "
case flagMapped:
froze = "mapped "
case flagFrozen | flagMapped:
froze = "frozen/mapped"
}
switch c.typeID {
case containerArray:
return fmt.Sprintf("<%sarray container, N=%d>", froze, c.N())
case containerBitmap:
return fmt.Sprintf("<%sbitmap container, N=%d, len %dx uint64>",
froze, c.N(), len(c.bitmap()))
case containerRun:
return fmt.Sprintf("<%srun container, N=%d, len %dx interval>",
froze, c.N(), len(c.runs()))
default:
return fmt.Sprintf("<unknown %s%d container, N=%d>", froze, c.typeID, c.N())
}
}
// NewContainer returns a new instance of container. This trivial function
// may later become more interesting.
func NewContainer() *Container {
statsHit("NewContainer")
c := &Container{typ: containerArray, len: 0, cap: stashedArraySize}
c := &Container{typeID: containerArray, len: 0, cap: stashedArraySize}
c.pointer = (*uint16)(unsafe.Pointer(&c.data[0]))
return c
}
// NewContainerBitmap makes a bitmap container using the provided bitmap, or
// an empty one if provided bitmap is nil. If the provided bitmap is too short,
// it will be padded.
func NewContainerBitmap(n int32, bitmap []uint64) *Container {
// it will be padded. This function's API is wrong; it should have been
// written as NewContainerBitmapN, and this should not take the n argument,
// but I did it wrong initially and now that would be a breaking change.
func NewContainerBitmap(n int, bitmap []uint64) *Container {
if bitmap == nil {
return NewContainerBitmapN(nil, 0)
}
// pad to required length
if len(bitmap) < bitmapN {
bm2 := make([]uint64, bitmapN)
copy(bm2, bitmap)
bitmap = bm2
}
c := &Container{typeID: containerBitmap}
c.setBitmap(bitmap)
// set n based on bitmap contents.
if n < 0 {
c.bitmapRepair()
} else {
c.setN(int32(n))
}
return c
}
// NewContainerBitmapN makes a bitmap container using the provided bitmap, or
// an empty one if provided bitmap is nil. If the provided bitmap is too short,
// it will be padded. The container's count is specified directly.
func NewContainerBitmapN(bitmap []uint64, n int32) *Container {
if bitmap == nil {
bitmap = make([]uint64, bitmapN)
}
@ -66,23 +127,40 @@ func NewContainerBitmap(n int32, bitmap []uint64) *Container {
copy(bm2, bitmap)
bitmap = bm2
}
c := &Container{typ: containerBitmap, n: n}
c := &Container{typeID: containerBitmap, n: n}
c.setBitmap(bitmap)
return c
}
// NewContainerArray returns an array using the provided set of values. It's
// okay if the slice is nil; that's a length of zero.
// NewContainerArray returns an array container using the provided set of
// values. It's okay if the slice is nil; that's a length of zero.
func NewContainerArray(set []uint16) *Container {
c := &Container{typ: containerArray, n: int32(len(set))}
c := &Container{typeID: containerArray, n: int32(len(set))}
c.setArray(set)
return c
}
// NewContainerRun creates a new run array using a provided (possibly nil)
// NewContainerArrayCopy returns an array container using the provided set of
// values. It's okay if the slice is nil; that's a length of zero. It copies
// the provided slice to new storage.
func NewContainerArrayCopy(set []uint16) *Container {
c := &Container{typeID: containerArray, n: int32(len(set))}
c.setArrayMaybeCopy(set, true)
return c
}
// NewContainerArrayN returns an array container using the specified
// set of values, but overriding n.
func NewContainerArrayN(set []uint16, n int32) *Container {
c := &Container{typeID: containerArray, n: n}
c.setArray(set)
return c
}
// NewContainerRun creates a new run container using a provided (possibly nil)
// slice of intervals.
func NewContainerRun(set []interval16) *Container {
c := &Container{typ: containerRun}
c := &Container{typeID: containerRun}
c.setRuns(set)
for _, run := range set {
c.n += int32(run.last-run.start) + 1
@ -90,61 +168,242 @@ func NewContainerRun(set []interval16) *Container {
return c
}
// NewContainerRunCopy creates a new run container using a provided (possibly nil)
// slice of intervals. It copies the provided slice to new storage.
func NewContainerRunCopy(set []interval16) *Container {
c := &Container{typeID: containerRun}
c.setRunsMaybeCopy(set, true)
for _, run := range set {
c.n += int32(run.last-run.start) + 1
}
return c
}
// NewContainerRunN creates a new run array using a provided (possibly nil)
// slice of intervals. It overrides n using the provided value.
func NewContainerRunN(set []interval16, n int32) *Container {
c := &Container{typeID: containerRun, n: n}
c.setRuns(set)
return c
}
// Mapped returns the internal mapped field, which indicates whether the
// slice's backing store is believed to be associated with unwriteable
// mmapped space.
func (c *Container) Mapped() bool {
return c.mapped
if c == nil {
return false
}
return (c.flags & flagMapped) != 0
}
// N returns the internal n field.
// frozen() returns the internal frozen state. It isn't exported because
// nothing outside this package should be thinking about this.
func (c *Container) frozen() bool {
if c == nil {
return true
}
return (c.flags & flagFrozen) != 0
}
// N returns the 1-count of the container.
func (c *Container) N() int32 {
if c == nil {
return 0
}
return c.n
}
func (c *Container) setN(n int32) {
if c == nil {
if roaringParanoia {
panic("trying to setN on a nil container")
}
return
}
c.n = n
}
func (c *Container) typ() byte {
if c == nil {
return containerNil
}
return c.typeID
}
// setTyp should only be called if you already know that c is a
// non-nil, non-frozen, container.
func (c *Container) setTyp(newType byte) {
if roaringParanoia {
if c == nil || c.frozen() {
panic("setTyp on nil or frozen container")
}
}
c.typeID = newType
}
func (c *Container) setMapped(mapped bool) {
if roaringParanoia {
if c == nil || c.frozen() {
panic("setMapped on nil or frozen container")
}
}
if mapped {
c.flags |= flagMapped
} else {
c.flags &^= flagMapped
}
}
// Freeze returns an unmodifiable container identical to c. This might
// be c, now marked unmodifiable, or might be a new container.
func (c *Container) Freeze() *Container {
if c == nil {
return nil
}
c.flags |= flagFrozen
return c
}
// Thaw returns a modifiable container identical to c. This may be c, or it
// may be a new container with distinct backing store.
func (c *Container) Thaw() *Container {
if roaringParanoia {
if c == nil {
panic("trying to thaw a nil container")
}
}
if c.flags&(flagFrozen|flagMapped) == 0 {
return c
}
return c.unmapOrClone()
}
func (c *Container) unmapOrClone() *Container {
if c.flags&flagFrozen != 0 {
// Caqn't modify this container, therefore, we have to make a
// copy.
return c.Clone()
}
c.flags &^= flagMapped
// mapped: we want to unmap the storage.
switch c.typeID {
case containerArray:
// mapped flag is wrong here
if c.pointer == (*uint16)(unsafe.Pointer(&c.data)) {
return c
}
// maybe it fits in storage
if c.len <= stashedArraySize {
copy(c.data[:stashedArraySize], c.array())
c.pointer, c.cap = (*uint16)(unsafe.Pointer(&c.data)), stashedArraySize
return c
}
array := c.array()
tmp := make([]uint16, c.len)
copy(tmp, array)
h := (*reflect.SliceHeader)(unsafe.Pointer(&tmp))
c.pointer, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Cap)
runtime.KeepAlive(&tmp)
case containerRun:
// mapped flag is wrong here
if c.pointer == (*uint16)(unsafe.Pointer(&c.data)) {
return c
}
oldRuns := c.runs()
// maybe it fits in storage
if c.len <= stashedRunSize {
c.pointer, c.cap = (*uint16)(unsafe.Pointer(&c.data)), stashedRunSize
copy(c.runs(), oldRuns)
return c
}
tmp := make([]interval16, c.len)
copy(tmp, oldRuns)
h := (*reflect.SliceHeader)(unsafe.Pointer(&tmp))
c.pointer, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Cap)
runtime.KeepAlive(&tmp)
case containerBitmap:
bitmap := c.bitmap()
tmp := make([]uint64, bitmapN)
copy(tmp, bitmap)
h := (*reflect.SliceHeader)(unsafe.Pointer(&tmp))
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(h.Data)), bitmapN, bitmapN
runtime.KeepAlive(&tmp)
default:
panic(fmt.Sprintf("can't thaw invalid container, type %d", c.typeID))
}
return c
}
// array yields the data viewed as a slice of uint16 values.
func (c *Container) array() []uint16 {
if roaringParanoia {
if c.typ != containerArray {
if c == nil {
panic("attempt to read a nil container's array")
}
if c.typeID != containerArray {
panic("attempt to read non-array's array")
}
}
return *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(unsafe.Pointer(c.pointer)), Len: int(c.len), Cap: int(c.cap)}))
}
// setArray stores a set of uint16s as data.
func (c *Container) setArray(array []uint16) {
// setArrayMaybeCopy stores a set of uint16s as data. c must not be frozen.
// If doCopy is set, it will ensure that the data get copied (possibly to
// its internal stash.)
func (c *Container) setArrayMaybeCopy(array []uint16, doCopy bool) {
if roaringParanoia {
if c.typ != containerArray {
if c == nil || c.frozen() {
panic("setArray on nil or frozen container")
}
if c.typeID != containerArray {
panic("attempt to write non-array's array")
}
}
// no array: start with our default 5-value array
if array == nil {
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(&c.data[0])), 0, stashedArraySize
c.n = c.len
return
}
h := (*reflect.SliceHeader)(unsafe.Pointer(&array))
if h.Data == uintptr(unsafe.Pointer(c.pointer)) {
// nothing to do but update length
c.len = int32(h.Len)
c.n = c.len
return
}
// array we can fit in data store:
if len(array) <= stashedArraySize {
copy(c.data[:stashedArraySize], array)
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(&c.data[0])), int32(len(array)), stashedArraySize
c.mapped = false // this is no longer using a hypothetical mmapped input array
c.n = c.len
c.flags &^= flagMapped // this is no longer using a hypothetical mmapped input array
return
}
// copy the array
if doCopy {
a2 := make([]uint16, len(array))
copy(a2, array)
h = (*reflect.SliceHeader)(unsafe.Pointer(&a2))
}
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Len), int32(h.Cap)
c.n = c.len
runtime.KeepAlive(&array)
}
// setArrayMaybeCopy stores a set of uint16s as data. c must not be frozen.
func (c *Container) setArray(array []uint16) {
c.setArrayMaybeCopy(array, false)
}
// bitmap yields the data viewed as a slice of uint64s holding bits.
func (c *Container) bitmap() []uint64 {
if roaringParanoia {
if c.typ != containerBitmap {
if c == nil {
panic("attempt to read nil container's bitmap")
}
if c.typeID != containerBitmap {
panic("attempt to read non-bitmap's bitmap")
}
}
@ -153,8 +412,11 @@ func (c *Container) bitmap() []uint64 {
// setBitmap stores a set of uint64s as data.
func (c *Container) setBitmap(bitmap []uint64) {
if c == nil || c.frozen() {
panic("setBitmap on nil or frozen container")
}
if roaringParanoia {
if c.typ != containerBitmap {
if c.typeID != containerBitmap {
panic("attempt to write non-bitmap's bitmap")
}
}
@ -166,17 +428,29 @@ func (c *Container) setBitmap(bitmap []uint64) {
// runs yields the data viewed as a slice of intervals.
func (c *Container) runs() []interval16 {
if roaringParanoia {
if c.typ != containerRun {
if c == nil {
panic("attempt to read nil container's runs")
}
if c.typeID != containerRun {
panic("attempt to read non-run's runs")
}
}
return *(*[]interval16)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(unsafe.Pointer(c.pointer)), Len: int(c.len), Cap: int(c.cap)}))
}
// setRuns stores a set of intervals as data.
// setRuns stores a set of intervals as data. c must not be frozen.
func (c *Container) setRuns(runs []interval16) {
c.setRunsMaybeCopy(runs, false)
}
// setRunsMaybeCopy stores a set of intervals as data. c must not be frozen.
// If doCopy is set, the values will be copied to different storage.
func (c *Container) setRunsMaybeCopy(runs []interval16, doCopy bool) {
if roaringParanoia {
if c.typ != containerRun {
if c == nil || c.frozen() {
panic("setRuns on nil or frozen container")
}
if c.typeID != containerRun {
panic("attempt to write non-run's runs")
}
}
@ -197,20 +471,64 @@ func (c *Container) setRuns(runs []interval16) {
newRuns := *(*[]interval16)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&c.data[0])), Len: stashedRunSize, Cap: stashedRunSize}))
copy(newRuns, runs)
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(&c.data[0])), int32(len(runs)), stashedRunSize
c.mapped = false // this is no longer using a hypothetical mmapped input array
c.flags &^= flagMapped // this is no longer using a hypothetical mmapped input array
return
}
if doCopy {
r2 := make([]interval16, len(runs))
copy(r2, runs)
h = (*reflect.SliceHeader)(unsafe.Pointer(&r2))
}
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Len), int32(h.Cap)
runtime.KeepAlive(&runs)
}
// Update updates the container
func (c *Container) Update(typ byte, n int32, mapped bool) {
c.typ = typ
// UpdateOrMake updates the container, yielding a new container if necessary.
func (c *Container) UpdateOrMake(typ byte, n int32, mapped bool) *Container {
if c == nil {
switch typ {
case containerRun:
c = NewContainerRunN(nil, n)
case containerBitmap:
c = NewContainerBitmapN(nil, n)
default:
c = NewContainerArrayN(nil, n)
}
c.flags |= flagMapped
return c
}
// ensure that we are allowed to modify this container
c = c.Thaw()
c.typeID = typ
c.n = n
c.mapped = mapped
// note: this probably shouldn't be happening, the decision should be getting
// made when we specify the storage.
c.setMapped(mapped)
// we don't know that any existing slice is usable, so let's ditch it
switch c.typ {
switch c.typeID {
case containerArray:
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(&c.data[0])), int32(0), stashedArraySize
case containerRun:
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(&c.data[0])), 0, stashedRunSize
default:
c.pointer, c.len, c.cap = nil, 0, 0
}
return c
}
// Update updates the container if possible. It is an error to
// call Update on a frozen container.
func (c *Container) Update(typ byte, n int32, mapped bool) {
if c == nil || c.frozen() {
panic("cannot Update a nil or frozen container")
}
c.typeID = typ
c.n = n
// note: this probably shouldn't be happening, the decision should be getting
// made when we specify the storage.
c.setMapped(mapped)
// we don't know that any existing slice is usable, so let's ditch it
switch c.typeID {
case containerArray:
c.pointer, c.len, c.cap = (*uint16)(unsafe.Pointer(&c.data[0])), int32(0), stashedArraySize
case containerRun:
@ -222,56 +540,30 @@ func (c *Container) Update(typ byte, n int32, mapped bool) {
// isArray returns true if the container is an array container.
func (c *Container) isArray() bool {
return c.typ == containerArray
if roaringParanoia {
if c == nil {
panic("calling isArray on nil container")
}
}
return c.typeID == containerArray
}
// isBitmap returns true if the container is a bitmap container.
func (c *Container) isBitmap() bool {
return c.typ == containerBitmap
if roaringParanoia {
if c == nil {
panic("calling isBitmap on nil container")
}
}
return c.typeID == containerBitmap
}
// isRun returns true if the container is a run-length-encoded container.
func (c *Container) isRun() bool {
return c.typ == containerRun
}
// unmapArray ensures that the container is not using mmapped storage.
func (c *Container) unmapArray() {
if !c.mapped {
return
if roaringParanoia {
if c == nil {
panic("calling isRun on nil container")
}
}
array := c.array()
tmp := make([]uint16, c.len)
copy(tmp, array)
h := (*reflect.SliceHeader)(unsafe.Pointer(&tmp))
c.pointer, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Cap)
runtime.KeepAlive(&tmp)
c.mapped = false
}
// unmapBitmap ensures that the container is not using mmapped storage.
func (c *Container) unmapBitmap() {
if !c.mapped {
return
}
bitmap := c.bitmap()
tmp := make([]uint64, c.len)
copy(tmp, bitmap)
h := (*reflect.SliceHeader)(unsafe.Pointer(&tmp))
c.pointer, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Cap)
runtime.KeepAlive(&tmp)
c.mapped = false
}
// unmapRun ensures that the container is not using mmapped storage.
func (c *Container) unmapRun() {
if !c.mapped {
return
}
runs := c.runs()
tmp := make([]interval16, c.len)
copy(tmp, runs)
h := (*reflect.SliceHeader)(unsafe.Pointer(&tmp))
c.pointer, c.cap = (*uint16)(unsafe.Pointer(h.Data)), int32(h.Cap)
c.mapped = false
return c.typeID == containerRun
}

View file

@ -15,6 +15,7 @@
package roaring
import (
"fmt"
"io"
)
@ -42,7 +43,7 @@ func NewBTreeBitmap(a ...uint64) *Bitmap {
func (btc *bTreeContainers) Get(key uint64) *Container {
// Check the last* cache for same container.
if key == btc.lastKey && btc.lastContainer != nil {
if key == btc.lastKey {
return btc.lastContainer
}
@ -57,11 +58,15 @@ func (btc *bTreeContainers) Get(key uint64) *Container {
}
func (btc *bTreeContainers) Put(key uint64, c *Container) {
// If we don't do this, a Put on a container we just got from
// Get can result in the tree containing a different container
// than we'll get on next lookup.
btc.lastKey, btc.lastContainer = key, c
// If a mapped container is added to the tree, reset the
// lastContainer cache so that the cache is not pointing
// at a read-only mmap.
if c.Mapped() {
btc.lastContainer = nil
btc.lastKey = ^uint64(0)
}
btc.tree.Set(key, c)
}
@ -69,13 +74,13 @@ func (btc *bTreeContainers) Put(key uint64, c *Container) {
func (u updater) update(oldV *Container, exists bool) (*Container, bool) {
// update the existing container
if exists {
oldV.Update(u.typ, u.n, u.mapped)
return oldV, false
oldV = oldV.UpdateOrMake(u.typ, u.n, u.mapped)
return oldV, true
}
cont := NewContainer()
cont.typ = u.typ
cont.n = u.n
cont.mapped = u.mapped
cont.setTyp(u.typ)
cont.setN(u.n)
cont.setMapped(u.mapped)
return cont, true
}
@ -98,7 +103,7 @@ func (btc *bTreeContainers) Remove(key uint64) {
func (btc *bTreeContainers) GetOrCreate(key uint64) *Container {
// Check the last* cache for same container.
if key == btc.lastKey && btc.lastContainer != nil {
if key == btc.lastKey {
return btc.lastContainer
}
@ -119,7 +124,7 @@ func (btc *bTreeContainers) Count() (n uint64) {
e, _ := btc.tree.Seek(0)
_, c, err := e.Next()
for err != io.EOF {
n += uint64(c.n)
n += uint64(c.N())
_, c, err = e.Next()
}
return n
@ -142,6 +147,23 @@ func (btc *bTreeContainers) Clone() Containers {
return nbtc
}
func (btc *bTreeContainers) Freeze() Containers {
nbtc := newBTreeContainers()
itr, err := btc.tree.SeekFirst()
if err == io.EOF {
return nbtc
}
for {
k, v, err := itr.Next()
if err == io.EOF {
break
}
nbtc.tree.Set(k, v.Freeze())
}
return nbtc
}
func (btc *bTreeContainers) First() (key uint64, c *Container) {
if btc.tree.Len() == 0 {
return 0, nil
@ -163,7 +185,10 @@ func (btc *bTreeContainers) Size() int {
func (btc *bTreeContainers) Reset() {
btc.tree = treeNew()
btc.lastKey = 0
// use a definitely-invalid key, so we can distinguish between "you
// just looked that up, and it was a nil container" and "you have
// never looked that up before."
btc.lastKey = ^uint64(0)
btc.lastContainer = nil
}
@ -187,6 +212,23 @@ func (btc *bTreeContainers) Repair() {
}
}
// Update calls fn (existing-container, existed), and expects
// (new-container, write). If write is true, the container is used to
// replace the given container.
func (btc *bTreeContainers) Update(key uint64, fn func(*Container, bool) (*Container, bool)) {
btc.tree.Put(key, fn)
}
// UpdateEvery calls fn (existing-container, existed), and expects
// (new-container, write). If write is true, the container is used to
// replace the given container.
func (btc *bTreeContainers) UpdateEvery(fn func(*Container, bool) (*Container, bool)) {
e, _ := btc.tree.Seek(0)
// currently not handling the error from this, but in practice it has
// to be io.EOF.
_ = e.Every(fn)
}
type btcIterator struct {
e *enumerator
key uint64
@ -194,19 +236,20 @@ type btcIterator struct {
}
func (i *btcIterator) Next() bool {
k, v, err := i.e.Next()
if err == io.EOF {
return false
}
if roaringParanoia {
if v == nil {
panic(fmt.Sprintf("got nil container for key %d", k))
}
}
i.key = k
i.val = v
return true
}
func (i *btcIterator) Value() (uint64, *Container) {
if i.val == nil {
return 0, nil
}
return i.key, i.val
}

View file

@ -50,15 +50,21 @@ func (sc *sliceContainers) PutContainerValues(key uint64, typ byte, n int, mappe
i := search64(sc.keys, key)
if i < 0 {
c := NewContainer()
c.typ = typ
c.n = int32(n)
c.mapped = mapped
c.setTyp(typ)
c.setN(int32(n))
c.setMapped(mapped)
sc.insertAt(key, c, -i-1)
} else {
c := sc.containers[i]
c.typ = typ
c.n = int32(n)
c.mapped = mapped
// if the container already exists, and is frozen, this may
// result in copying its data, which is sort of pointless
// because PutContainerValues almost always gets called
// because we're reading new data from a file -- but also
// that means this case probably never happens.
c := sc.containers[i].Thaw()
c.setTyp(typ)
c.setN(int32(n))
c.setMapped(mapped)
sc.containers[i] = c
}
}
@ -114,6 +120,17 @@ func (sc *sliceContainers) Clone() Containers {
return other
}
func (sc *sliceContainers) Freeze() Containers {
other := newSliceContainers()
other.keys = make([]uint64, len(sc.keys))
other.containers = make([]*Container, len(sc.containers))
copy(other.keys, sc.keys)
for i, c := range sc.containers {
other.containers[i] = c.Freeze()
}
return other
}
func (sc *sliceContainers) First() (key uint64, c *Container) {
if len(sc.keys) == 0 {
return 0, nil
@ -136,7 +153,7 @@ func (sc *sliceContainers) Size() int {
func (sc *sliceContainers) Count() uint64 {
n := uint64(0)
for i := range sc.containers {
n += uint64(sc.containers[i].n)
n += uint64(sc.containers[i].N())
}
return n
}
@ -169,6 +186,42 @@ func (sc *sliceContainers) Repair() {
}
}
// Update calls fn (existing-container, existed), and expects
// (new-container, write). If write is true, the container is used to
// replace the given container.
func (sc *sliceContainers) Update(key uint64, fn func(*Container, bool) (*Container, bool)) {
i, found := sc.seek(key)
var nc *Container
var write bool
if found {
nc, write = fn(sc.containers[i], true)
if write {
sc.containers[i] = nc
}
} else {
nc, write = fn(nil, false)
// don't expand the slice just to add a nil container, we
// could return that anyway
if write && nc != nil {
sc.containers = append(sc.containers, nil)
copy(sc.containers[i+1:], sc.containers[i:])
sc.containers[i] = nc
}
}
}
// UpdateEvery calls fn (existing-container, existed), and expects
// (new-container, write). If write is true, the container is used to
// replace the given container.
func (sc *sliceContainers) UpdateEvery(fn func(*Container, bool) (*Container, bool)) {
for i, c := range sc.containers {
nc, write := fn(c, true)
if write {
sc.containers[i] = nc
}
}
}
type sliceIterator struct {
e *sliceContainers
i int
@ -177,14 +230,20 @@ type sliceIterator struct {
}
func (si *sliceIterator) Next() bool {
if si.e == nil || si.i > len(si.e.keys)-1 {
if si.e == nil {
return false
}
si.key = si.e.keys[si.i]
si.value = si.e.containers[si.i]
si.i++
return true
// discard nil containers from iteration. we don't always
// actually remove them because copying is expensive.
for si.i < len(si.e.keys) {
si.key = si.e.keys[si.i]
si.value = si.e.containers[si.i]
si.i++
if si.value != nil {
return true
}
}
return false
}
func (si *sliceIterator) Value() (uint64, *Container) {

View file

@ -44,14 +44,14 @@ func testContainersIterator(cs Containers, t *testing.T) {
if !itr.Next() {
t.Fatalf("one should be next, but got false")
}
if key, val := itr.Value(); key != 1 || val.n != 1 {
t.Fatalf("Wrong k/v, exp: 1,1 got: %v,%v", key, val.n)
if key, val := itr.Value(); key != 1 || val.N() != 1 {
t.Fatalf("Wrong k/v, exp: 1,1 got: %v,%v", key, val.N())
}
if !itr.Next() {
t.Fatalf("two should be next, but got false")
}
if key, val := itr.Value(); key != 2 || val.n != 2 {
t.Fatalf("Wrong k/v, exp: 2,2 got: %v,%v", key, val.n)
if key, val := itr.Value(); key != 2 || val.N() != 2 {
t.Fatalf("Wrong k/v, exp: 2,2 got: %v,%v", key, val.N())
}
if itr.Next() {
@ -69,14 +69,14 @@ func testContainersIterator(cs Containers, t *testing.T) {
if !found {
t.Fatalf("should have found 3")
}
if key, val := itr.Value(); key != 3 || val.n != 3 {
t.Fatalf("Wrong k/v, exp: 3,3 got: %v,%v", key, val.n)
if key, val := itr.Value(); key != 3 || val.N() != 3 {
t.Fatalf("Wrong k/v, exp: 3,3 got: %v,%v", key, val.N())
}
if !itr.Next() {
t.Fatalf("5 should be next, but got false")
}
if key, val := itr.Value(); key != 5 || val.n != 5 {
t.Fatalf("Wrong k/v, exp: 5,5 got: %v,%v", key, val.n)
if key, val := itr.Value(); key != 5 || val.N() != 5 {
t.Fatalf("Wrong k/v, exp: 5,5 got: %v,%v", key, val.N())
}
itr, found = cs.Iterator(4)
@ -86,14 +86,14 @@ func testContainersIterator(cs Containers, t *testing.T) {
if !itr.Next() {
t.Fatalf("5 should be next, but got false")
}
if key, val := itr.Value(); key != 5 || val.n != 5 {
t.Fatalf("Wrong k/v, exp: 5,5 got: %v,%v", key, val.n)
if key, val := itr.Value(); key != 5 || val.N() != 5 {
t.Fatalf("Wrong k/v, exp: 5,5 got: %v,%v", key, val.N())
}
if !itr.Next() {
t.Fatalf("6 should be next, but got false")
}
if key, val := itr.Value(); key != 6 || val.n != 6 {
t.Fatalf("Wrong k/v, exp: 6,6 got: %v,%v", key, val.n)
if key, val := itr.Value(); key != 6 || val.N() != 6 {
t.Fatalf("Wrong k/v, exp: 6,6 got: %v,%v", key, val.N())
}
if itr.Next() {

File diff suppressed because it is too large Load diff

View file

@ -253,8 +253,7 @@ func doContainer(typ byte, data interface{}) *Container {
case containerArray:
return NewContainerArray(data.([]uint16))
case containerBitmap:
c := NewContainerBitmap(0, data.([]uint64))
c.n = c.count()
c := NewContainerBitmap(-1, data.([]uint64))
return c
case containerRun:
return NewContainerRun(data.([]interval16))

View file

@ -32,10 +32,6 @@ func (iv interval16) String() string {
return fmt.Sprintf("[%d, %d]", iv.start, iv.last)
}
func (c *Container) String() string {
return fmt.Sprintf("<%s container n=%d, array[%d], runs[%d], bitmap[%d]> type:%s", c.info().Type, c.n, len(c.array()), len(c.runs()), len(c.bitmap()), containerTypeNames[c.typ])
}
func TestRunAppendInterval(t *testing.T) {
a := NewContainerRun(nil)
tests := []struct {
@ -102,15 +98,15 @@ func TestContainerRunAdd(t *testing.T) {
{8, []interval16{{start: 0, last: 4}, {start: 6, last: 8}, {start: 10, last: 10}}},
}
for _, test := range tests {
c.mapped = true
ret := c.add(test.op)
if !ret {
c.setMapped(true)
c, changed := c.add(test.op)
if !changed {
t.Fatalf("result of adding new bit should be true: %v", c.runs())
}
if !reflect.DeepEqual(c.runs(), test.exp) {
t.Fatalf("Should have %v, but got %v after adding %v", test.exp, c.runs(), test.op)
}
if c.mapped {
if c.Mapped() {
t.Fatalf("container should not be mapped after adding bit %v", test.op)
}
}
@ -118,14 +114,14 @@ func TestContainerRunAdd(t *testing.T) {
func TestContainerRunAdd2(t *testing.T) {
c := NewContainerRun(nil)
ret := c.add(0)
c, ret := c.add(0)
if !ret {
t.Fatalf("result of adding new bit should be true: %v", c.runs())
}
if !reflect.DeepEqual(c.runs(), []interval16{{start: 0, last: 0}}) {
t.Fatalf("should have 1 run of length 1, but have %v", c.runs())
}
ret = c.add(0)
c, ret = c.add(0)
if ret {
t.Fatalf("result of adding existing bit should be false: %v", c.runs())
}
@ -237,23 +233,23 @@ func TestBitmapCountRange(t *testing.T) {
}
func TestIntersectionCountArrayBitmap3(t *testing.T) {
a, b := NewContainerBitmap(maxContainerVal+1, getFullBitmap()), NewContainerBitmap(maxContainerVal+1, getFullBitmap())
a, b := NewContainerBitmapN(getFullBitmap(), maxContainerVal+1), NewContainerBitmapN(getFullBitmap(), maxContainerVal+1)
res := intersectBitmapBitmap(a, b)
if res.n != res.count() || res.n != maxContainerVal+1 {
t.Fatalf("test #1 intersectCountBitmapBitmap fail orig: %v new: %v exp: %v", res.n, res.count(), maxContainerVal+1)
if res.N() != res.count() || res.N() != maxContainerVal+1 {
t.Fatalf("test #1 intersectCountBitmapBitmap fail orig: %v new: %v exp: %v", res.N(), res.count(), maxContainerVal+1)
}
a.bitmapToRun(0)
a = a.bitmapToRun(0)
res = intersectBitmapRun(b, a)
if res.n != res.count() || res.n != maxContainerVal+1 {
t.Fatalf("test #2 intersectCountBitmapRun fail orig: %v new: %v exp: %v", res.n, res.count(), maxContainerVal+1)
if res.N() != res.count() || res.N() != maxContainerVal+1 {
t.Fatalf("test #2 intersectCountBitmapRun fail orig: %v new: %v exp: %v", res.N(), res.count(), maxContainerVal+1)
}
b.bitmapToRun(0)
res = intersectRunRun(a, b)
n := intersectionCountRunRun(a, b)
if res.n != res.count() || res.n != maxContainerVal+1 || res.n != int32(n) {
t.Fatalf("test #3 intersectCountRunRun fail orig: %v new: %v exp: %v", res.n, res.count(), maxContainerVal+1)
if res.N() != res.count() || res.N() != maxContainerVal+1 || res.N() != int32(n) {
t.Fatalf("test #3 intersectCountRunRun fail orig: %v new: %v exp: %v", res.N(), res.count(), maxContainerVal+1)
}
}
@ -321,15 +317,16 @@ func TestRunRemove(t *testing.T) {
}
for i, test := range tests {
c.mapped = true
ret := c.remove(test.op)
c = c.Freeze()
var ret bool
c, ret = c.remove(test.op)
if ret != test.expRet || !reflect.DeepEqual(c.runs(), test.exp) {
t.Fatalf("test #%v Unexpected result removing %v from runs. Expected %v, got %v. Expected %v, got %v", i, test.op, test.expRet, ret, test.exp, c.runs())
}
if ret && c.mapped {
if ret && c.frozen() {
t.Fatalf("test #%v container was not unmapped although bit %v was removed", i, test.op)
}
if !ret && !c.mapped {
if !ret && !c.frozen() {
t.Fatalf("test #%v container was unmapped although bit %v was not removed", i, test.op)
}
}
@ -370,7 +367,7 @@ func TestIntersectionCountBitmapRun(t *testing.T) {
t.Fatalf("count of %v with %v should be 1, but got %v", a.bitmap(), b.runs(), ret)
}
a = NewContainerBitmap(29, []uint64{0xF0000001, 0xFF00000000000000, 0xFF000000000000F0, 0x0F0000})
a = NewContainerBitmap(-1, []uint64{0xF0000001, 0xFF00000000000000, 0xFF000000000000F0, 0x0F0000})
b = NewContainerRun([]interval16{{start: 29, last: 31}, {start: 125, last: 134}, {start: 191, last: 197}, {start: 200, last: 300}})
ret = intersectionCountBitmapRun(a, b)
@ -380,8 +377,6 @@ func TestIntersectionCountBitmapRun(t *testing.T) {
}
func TestIntersectionCountRunRun(t *testing.T) {
a := NewContainerRun(nil)
b := NewContainerRun(nil)
tests := []struct {
aruns []interval16
bruns []interval16
@ -419,10 +414,8 @@ func TestIntersectionCountRunRun(t *testing.T) {
bruns: []interval16{{start: 9, last: 9}, {start: 11, last: 17}}, exp: 6},
}
for i, test := range tests {
a.typ = containerRun
b.typ = containerRun
a.setRuns(test.aruns)
b.setRuns(test.bruns)
a := NewContainerRun(test.aruns)
b := NewContainerRun(test.bruns)
ret := intersectionCountRunRun(a, b)
if ret != test.exp {
t.Fatalf("test #%v failed intersecting %v with %v should be %v, but got %v", i, test.aruns, test.bruns, test.exp, ret)
@ -524,8 +517,8 @@ func TestIntersectRunRun(t *testing.T) {
a.setRuns(test.aruns)
b.setRuns(test.bruns)
ret := intersectRunRun(a, b)
if ret.n != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.n)
if ret.N() != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.N())
}
if test.exp != nil {
if !reflect.DeepEqual(ret.runs(), test.exp) {
@ -538,8 +531,6 @@ func TestIntersectRunRun(t *testing.T) {
}
func TestIntersectBitmapRunBitmap(t *testing.T) {
a := NewContainerBitmap(0, nil)
b := NewContainerRun(nil)
tests := []struct {
bitmap []uint64
runs []interval16
@ -578,13 +569,11 @@ func TestIntersectBitmapRunBitmap(t *testing.T) {
},
}
for i, test := range tests {
copy(a.bitmap(), test.bitmap)
b.setRuns(test.runs)
b.n = 4097 // ;)
exp := make([]uint64, bitmapN)
copy(exp, test.exp)
a.typ = containerBitmap
b.typ = containerRun
a := NewContainerBitmap(-1, test.bitmap)
b := NewContainerRun(test.runs)
b.setN(4097)
ret := intersectBitmapRun(a, b)
if ret.isArray() {
ret.arrayToBitmap()
@ -592,8 +581,8 @@ func TestIntersectBitmapRunBitmap(t *testing.T) {
if !reflect.DeepEqual(ret.bitmap(), exp) {
t.Fatalf("test #%v expected %v, but got %v", i, exp, ret.bitmap())
}
if ret.n != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.n)
if ret.N() != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.N())
}
}
@ -646,8 +635,8 @@ func TestIntersectBitmapRunArray(t *testing.T) {
if !reflect.DeepEqual(ret.array(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.array())
}
if ret.n != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.n)
if ret.N() != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.N())
}
}
@ -738,8 +727,7 @@ func TestDifferenceMixed(t *testing.T) {
b := NewContainerArray([]uint16{0, 2, 4, 6, 8, 10, 12})
c := NewContainerBitmap(0, MakeBitmap([]uint64{0x64}))
c.n = c.countRange(0, 100)
c := NewContainerBitmap(-1, MakeBitmap([]uint64{0x64}))
d := NewContainerArray([]uint16{1, 3, 5, 7, 9, 11, 12})
@ -780,13 +768,13 @@ func TestDifferenceMixed(t *testing.T) {
}
res = difference(b, b)
if res.n != 0 {
t.Fatalf("test #8 expected 0, but got %d", res.n)
if res.N() != 0 {
t.Fatalf("test #8 expected 0, but got %d", res.N())
}
res = difference(c, c)
if res.n != 0 {
t.Fatalf("test #9 expected 0, but got %d", res.n)
if res.N() != 0 {
t.Fatalf("test #9 expected 0, but got %d", res.N())
}
res = difference(d, b)
@ -901,7 +889,6 @@ func TestUnionArrayRun(t *testing.T) {
}
func TestBitmapSetRange(t *testing.T) {
c := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
start uint64
@ -926,21 +913,18 @@ func TestBitmapSetRange(t *testing.T) {
}
for i, test := range tests {
bitmap := c.bitmap()
copy(bitmap, test.bitmap)
c.n = c.countRange(0, 65535)
c.bitmapSetRange(bitmap, test.start, test.last+1)
c := NewContainerBitmap(-1, test.bitmap)
c.bitmapSetRange(test.start, test.last+1)
if !reflect.DeepEqual(c.bitmap()[:len(test.exp)], test.exp) {
t.Fatalf("test %#v expected %x, got %x", i, test.exp, c.bitmap()[:len(test.bitmap)])
}
if test.expN != c.n {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.n)
if test.expN != c.N() {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.N())
}
}
}
func TestArrayToBitmap(t *testing.T) {
a := NewContainerArray(nil)
tests := []struct {
array []uint16
exp []uint64
@ -958,19 +942,15 @@ func TestArrayToBitmap(t *testing.T) {
for i, test := range tests {
exp := make([]uint64, bitmapN)
copy(exp, test.exp)
a.setArray(test.array)
a.n = int32(len(test.array))
a.arrayToBitmap()
a := NewContainerArray(test.array)
a = a.arrayToBitmap()
if !reflect.DeepEqual(a.bitmap(), exp) {
t.Fatalf("test #%v expected %v, but got %v", i, exp, a.bitmap())
}
a.bitmapToArray()
}
}
func TestBitmapToArray(t *testing.T) {
a := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
exp []uint16
@ -985,25 +965,16 @@ func TestBitmapToArray(t *testing.T) {
},
}
for i, test := range tests {
a.setBitmap(make([]uint64, bitmapN))
bitmap := a.bitmap()
n := int32(0)
for i, v := range test.bitmap {
bitmap[i] = v
n += int32(popcount(v))
}
a.n = n
a := NewContainerBitmap(-1, test.bitmap)
a.bitmapToArray()
a = a.bitmapToArray()
if !reflect.DeepEqual(a.array(), test.exp) {
t.Fatalf("test #%v expected %#v, but got %#v", i, test.exp, a.array())
}
a.arrayToBitmap()
}
}
func TestRunToBitmap(t *testing.T) {
a := NewContainerRun(nil)
tests := []struct {
runs []interval16
exp []uint64
@ -1037,11 +1008,8 @@ func TestRunToBitmap(t *testing.T) {
exp[i] = v
n += int(popcount(v))
}
a.typ = containerRun
a.setRuns(test.runs)
a.n = int32(n)
a.runToBitmap()
a := NewContainerRun(test.runs)
a = a.runToBitmap()
if !reflect.DeepEqual(a.bitmap(), exp) {
t.Fatalf("test #%v expected %v, but got %v", i, exp, a.bitmap())
}
@ -1058,7 +1026,6 @@ func getFullBitmap() []uint64 {
}
func TestBitmapToRun(t *testing.T) {
a := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
exp []interval16
@ -1116,15 +1083,8 @@ func TestBitmapToRun(t *testing.T) {
tests[8].bitmap[1023] = 0xFFFFFFFFFFFFFFFF
for i, test := range tests {
a.setBitmap(make([]uint64, bitmapN))
bitmap := a.bitmap()
n := 0
for i, v := range test.bitmap {
bitmap[i] = v
n += int(popcount(v))
}
a.n = int32(n)
x := bitmap
a := NewContainerBitmap(-1, test.bitmap)
x := a.bitmap()
a.bitmapToRun(0)
if !reflect.DeepEqual(a.runs(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, a.runs())
@ -1137,7 +1097,6 @@ func TestBitmapToRun(t *testing.T) {
}
func TestArrayToRun(t *testing.T) {
a := NewContainerArray(nil)
tests := []struct {
array []uint16
exp []interval16
@ -1161,10 +1120,8 @@ func TestArrayToRun(t *testing.T) {
}
for i, test := range tests {
a.typ = containerArray
a.setArray(test.array)
a.n = int32(len(test.array))
a.arrayToRun(0)
a := NewContainerArray(test.array)
a = a.arrayToRun(0)
if !reflect.DeepEqual(a.runs(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, a.runs())
}
@ -1172,7 +1129,6 @@ func TestArrayToRun(t *testing.T) {
}
func TestRunToArray(t *testing.T) {
a := NewContainerRun(nil)
tests := []struct {
runs []interval16
exp []uint16
@ -1196,10 +1152,8 @@ func TestRunToArray(t *testing.T) {
}
for i, test := range tests {
a.typ = containerRun
a.setRuns(test.runs)
a.n = int32(len(test.exp))
a.runToArray()
a := NewContainerRun(test.runs)
a = a.runToArray()
if !reflect.DeepEqual(a.array(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, a.array())
}
@ -1207,7 +1161,6 @@ func TestRunToArray(t *testing.T) {
}
func TestBitmapZeroRange(t *testing.T) {
c := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
start uint64
@ -1230,17 +1183,16 @@ func TestBitmapZeroRange(t *testing.T) {
expN: 13,
},
}
bitmap := c.bitmap()
for i, test := range tests {
copy(bitmap, test.bitmap)
c.n = c.countRange(0, 65535)
c := NewContainerBitmap(-1, test.bitmap)
bitmap := c.bitmap()
c.bitmapZeroRange(test.start, test.last+1)
if !reflect.DeepEqual(bitmap[:len(test.exp)], test.exp) {
t.Fatalf("test %#v expected %x, got %x", i, test.exp, bitmap[:len(test.bitmap)])
}
if test.expN != c.n {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.n)
if test.expN != c.N() {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.N())
}
for i := range test.bitmap {
bitmap[i] = 0
@ -1250,8 +1202,6 @@ func TestBitmapZeroRange(t *testing.T) {
}
func TestUnionBitmapRun(t *testing.T) {
a := NewContainerBitmap(0, nil)
b := NewContainerRun(nil)
tests := []struct {
bitmap []uint64
runs []interval16
@ -1266,20 +1216,18 @@ func TestUnionBitmapRun(t *testing.T) {
},
}
for i, test := range tests {
copy(a.bitmap(), test.bitmap)
a.n = a.bitmapCountRange(0, 65535)
b.setRuns(test.runs)
b.n = b.runCountRange(0, 65535)
a := NewContainerBitmap(-1, test.bitmap)
b := NewContainerRun(test.runs)
ret := unionBitmapRun(a, b)
if ret.isArray() {
ret.arrayToBitmap()
ret = ret.arrayToBitmap()
}
bitmap := ret.bitmap()
if !reflect.DeepEqual(bitmap[:len(test.exp)], test.exp) {
t.Fatalf("test #%v expected %x, but got %x", i, test.exp, bitmap[:len(test.exp)])
}
if ret.n != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.n)
if ret.N() != test.expN {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.N())
}
for i := range test.bitmap {
a.bitmap()[i] = 0
@ -1288,7 +1236,6 @@ func TestUnionBitmapRun(t *testing.T) {
}
func TestBitmapCountRuns(t *testing.T) {
c := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
exp int32
@ -1310,10 +1257,10 @@ func TestBitmapCountRuns(t *testing.T) {
exp: 10,
},
}
var c *Container
for i, test := range tests {
copy(c.bitmap(), test.bitmap)
c = NewContainerBitmap(-1, test.bitmap)
ret := c.bitmapCountRuns()
if ret != test.exp {
t.Fatalf("test #%v expected %v but got %v", i, test.exp, ret)
@ -1377,8 +1324,6 @@ func TestArrayCountRuns(t *testing.T) {
}
func TestDifferenceArrayRun(t *testing.T) {
a := NewContainerArray(nil)
b := NewContainerRun(nil)
tests := []struct {
array []uint16
runs []interval16
@ -1391,10 +1336,8 @@ func TestDifferenceArrayRun(t *testing.T) {
},
}
for i, test := range tests {
a.setArray(test.array)
a.n = int32(len(a.array()))
b.setRuns(test.runs)
b.n = b.runCountRange(0, 100)
a := NewContainerArray(test.array)
b := NewContainerRun(test.runs)
ret := differenceArrayRun(a, b)
if !reflect.DeepEqual(ret.array(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.array())
@ -1403,8 +1346,6 @@ func TestDifferenceArrayRun(t *testing.T) {
}
func TestDifferenceRunArray(t *testing.T) {
a := NewContainerRun(nil)
b := NewContainerArray(nil)
tests := []struct {
runs []interval16
array []uint16
@ -1457,10 +1398,8 @@ func TestDifferenceRunArray(t *testing.T) {
},
}
for i, test := range tests {
a.setRuns(test.runs)
a.n = a.runCountRange(0, 100)
b.setArray(test.array)
b.n = int32(len(b.array()))
a := NewContainerRun(test.runs)
b := NewContainerArray(test.array)
ret := differenceRunArray(a, b)
if !reflect.DeepEqual(ret.runs(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.runs())
@ -1480,8 +1419,6 @@ func MakeLastBitSet() []uint64 {
}
func TestDifferenceRunBitmap(t *testing.T) {
a := NewContainerRun(nil)
b := NewContainerBitmap(0, nil)
tests := []struct {
runs []interval16
bitmap []uint64
@ -1529,10 +1466,8 @@ func TestDifferenceRunBitmap(t *testing.T) {
},
}
for i, test := range tests {
a.setRuns(test.runs)
a.n = a.runCountRange(0, 65536)
copy(b.bitmap(), test.bitmap)
b.n = b.bitmapCountRange(0, 65536)
a := NewContainerRun(test.runs)
b := NewContainerBitmap(-1, test.bitmap)
ret := differenceRunBitmap(a, b)
if !reflect.DeepEqual(ret.runs(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.runs())
@ -1541,8 +1476,6 @@ func TestDifferenceRunBitmap(t *testing.T) {
}
func TestDifferenceBitmapRun(t *testing.T) {
a := NewContainerBitmap(0, nil)
b := NewContainerRun(nil)
tests := []struct {
bitmap []uint64
runs []interval16
@ -1610,10 +1543,8 @@ func TestDifferenceBitmapRun(t *testing.T) {
},
}
for i, test := range tests {
copy(a.bitmap(), test.bitmap)
a.n = a.bitmapCountRange(0, 65536)
b.setRuns(test.runs)
b.n = b.runCountRange(0, 65536)
a := NewContainerBitmap(-1, test.bitmap)
b := NewContainerRun(test.runs)
ret := differenceBitmapRun(a, b)
if !reflect.DeepEqual(ret.bitmap()[:len(test.exp)], test.exp) {
t.Fatalf("test #%v expected \n%X, but got \n%X", i, test.exp, ret.bitmap()[:len(test.exp)])
@ -1622,8 +1553,6 @@ func TestDifferenceBitmapRun(t *testing.T) {
}
func TestDifferenceBitmapArray(t *testing.T) {
b := NewContainerBitmap(0, nil)
a := NewContainerArray(nil)
tests := []struct {
bitmap []uint64
array []uint16
@ -1661,9 +1590,8 @@ func TestDifferenceBitmapArray(t *testing.T) {
},
}
for i, test := range tests {
b.bitmap()[0] = test.bitmap[0]
b.n = b.count()
a.setArray(test.array)
b := NewContainerBitmap(-1, test.bitmap[:1])
a := NewContainerArray(test.array)
ret := differenceBitmapArray(b, a)
if !reflect.DeepEqual(ret.array(), test.exp) {
t.Fatalf("test #%v expected %#v, but got %#v", i, test.exp, ret.array())
@ -1672,8 +1600,6 @@ func TestDifferenceBitmapArray(t *testing.T) {
}
func TestDifferenceBitmapBitmap(t *testing.T) {
a := NewContainerBitmap(0, nil)
b := NewContainerBitmap(0, nil)
tests := []struct {
abitmap []uint64
bbitmap []uint64
@ -1691,9 +1617,8 @@ func TestDifferenceBitmapBitmap(t *testing.T) {
},
}
for i, test := range tests {
a.bitmap()[0] = test.abitmap[0]
b.bitmap()[0] = test.bbitmap[0]
a := NewContainerBitmap(-1, test.abitmap)
b := NewContainerBitmap(-1, test.bbitmap)
ret := differenceBitmapBitmap(a, b)
if !reflect.DeepEqual(ret.array(), test.exp) {
t.Fatalf("test #%v expected \n%X, but got \n%X", i, test.exp, ret.array())
@ -1702,8 +1627,6 @@ func TestDifferenceBitmapBitmap(t *testing.T) {
}
func TestDifferenceRunRun(t *testing.T) {
a := NewContainerRun(nil)
b := NewContainerRun(nil)
tests := []struct {
aruns []interval16
bruns []interval16
@ -1721,16 +1644,14 @@ func TestDifferenceRunRun(t *testing.T) {
},
}
for i, test := range tests {
a.setRuns(test.aruns)
a.n = a.runCountRange(0, 100)
b.setRuns(test.bruns)
b.n = b.runCountRange(0, 100)
a := NewContainerRun(test.aruns)
b := NewContainerRun(test.bruns)
ret := differenceRunRun(a, b)
if !reflect.DeepEqual(ret.runs(), test.exp) {
t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.runs())
}
if ret.n != test.expn {
t.Fatalf("test #%v expected n=%v, but got n=%v", i, test.expn, ret.n)
if ret.N() != test.expn {
t.Fatalf("test #%v expected n=%v, but got n=%v", i, test.expn, ret.N())
}
}
}
@ -1756,7 +1677,7 @@ func TestWriteReadArray(t *testing.T) {
func TestWriteReadBitmap(t *testing.T) {
// create bitmap containing > 4096 bits
cb := NewContainerBitmap(129*32, nil)
cb := NewContainerBitmapN(nil, 129*32)
for i := 0; i < 129; i++ {
cb.bitmap()[i] = 0x5555555555555555
}
@ -1779,7 +1700,7 @@ func TestWriteReadBitmap(t *testing.T) {
func TestWriteReadFullBitmap(t *testing.T) {
// create bitmap containing > 4096 bits
cb := NewContainerBitmap(65536, nil)
cb := NewContainerBitmapN(nil, 65536)
for i := 0; i < bitmapN; i++ {
cb.bitmap()[i] = 0xffffffffffffffff
}
@ -1802,11 +1723,11 @@ func TestWriteReadFullBitmap(t *testing.T) {
t.Fatalf("bitmap test expected %x, but got %x", cb.bitmap(), bb2.Containers.Get(0).bitmap())
}
if bb2.Containers.Get(0).n != cb.n {
t.Fatalf("bitmap test expected count %x, but got %x", cb.n, bb2.Containers.Get(0).n)
if bb2.Containers.Get(0).N() != cb.N() {
t.Fatalf("bitmap test expected count %x, but got %x", cb.N(), bb2.Containers.Get(0).N())
}
if bb2.Containers.Get(0).count() != cb.count() {
t.Fatalf("bitmap test expected count %x, but got %x", cb.n, bb2.Containers.Get(0).n)
t.Fatalf("bitmap test expected count %x, but got %x", cb.N(), bb2.Containers.Get(0).N())
}
}
@ -1855,8 +1776,8 @@ func TestXorArrayRun(t *testing.T) {
}
for i, test := range tests {
test.a.n = test.a.count()
test.b.n = test.b.count()
test.a.setN(test.a.count())
test.b.setN(test.b.count())
ret := xor(test.a, test.b)
if !reflect.DeepEqual(ret.array(), test.exp.array()) {
t.Fatalf("test #%v expected %#v, but got %#v", i, test.exp, ret)
@ -1982,7 +1903,6 @@ func TestXorRunRun(t *testing.T) {
}
func TestBitmapXorRange(t *testing.T) {
c := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
start uint64
@ -2035,14 +1955,13 @@ func TestBitmapXorRange(t *testing.T) {
}
for i, test := range tests {
copy(c.bitmap(), test.bitmap)
c.n = c.countRange(0, 65535)
c := NewContainerBitmap(-1, test.bitmap)
c.bitmapXorRange(test.start, test.last+1)
if !reflect.DeepEqual(c.bitmap()[:len(test.exp)], test.exp) {
t.Fatalf("test %#v expected %x, got %x", i, test.exp, c.bitmap()[:len(test.bitmap)])
}
if test.expN != c.n {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.n)
if test.expN != c.N() {
t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.N())
}
}
}
@ -2060,15 +1979,21 @@ func TestXorBitmapRun(t *testing.T) {
},
}
for i, test := range tests {
a := NewContainerBitmap(0, test.bitmap)
e := NewContainerBitmap(0, test.exp)
a := NewContainerBitmap(-1, test.bitmap)
e := NewContainerBitmap(-1, test.exp)
b := NewContainerRun(test.runs)
//xorBitmapRun
ret := xor(a, b)
if ret.isRun() {
ret = ret.runToBitmap()
}
if !reflect.DeepEqual(ret.bitmap(), e.bitmap()) {
t.Fatalf("test #%v expected %v, but got %v", i, e.bitmap(), ret.bitmap())
}
ret = xor(b, a)
if ret.isRun() {
ret = ret.runToBitmap()
}
if !reflect.DeepEqual(ret.bitmap(), e.bitmap()) {
t.Fatalf("test #%v.1 expected %v, but got %v", i, e.bitmap(), ret.bitmap())
}
@ -2397,15 +2322,14 @@ func TestRunBinSearch(t *testing.T) {
}
func TestBitmap_RemoveEmptyContainers(t *testing.T) {
bm1 := NewFileBitmap(1<<16, 2<<16, 3<<16)
if _, err := bm1.Remove(2 << 16); err != nil {
t.Fatalf("removing a bit: %v", err)
}
if bm1.countEmptyContainers() != 1 {
bm2 := NewFileBitmap(1<<16, 2<<16+1, 3<<16)
bm3 := bm1.Intersect(bm2)
if bm3.countEmptyContainers() != 1 {
t.Fatalf("Should be 1 empty container ")
}
bm1.removeEmptyContainers()
bm3.removeEmptyContainers()
if bm1.countEmptyContainers() != 0 {
if bm3.countEmptyContainers() != 0 {
t.Fatalf("Should be no empty containers ")
}
}
@ -2510,7 +2434,6 @@ func TestSearch64(t *testing.T) {
}
func TestIntersectArrayBitmap(t *testing.T) {
a, b := NewContainerArray(nil), NewContainerBitmap(0, nil)
tests := []struct {
array []uint16
bitmap []uint64
@ -2554,8 +2477,8 @@ func TestIntersectArrayBitmap(t *testing.T) {
}
for i, test := range tests {
a.setArray(test.array)
copy(b.bitmap(), test.bitmap)
a := NewContainerArray(test.array)
b := NewContainerBitmap(-1, test.bitmap)
ret := intersectArrayBitmap(a, b).array()
if len(ret) == 0 && len(test.exp) == 0 {
continue
@ -3274,42 +3197,48 @@ func TestContainerCombinations(t *testing.T) {
for _, ct := range containerTypes {
clone := ret.Clone()
if ct == containerArray {
if clone.isBitmap() {
clone.bitmapToArray()
if clone == nil {
clone = NewContainerArray(nil)
} else if clone.isBitmap() {
clone = clone.bitmapToArray()
} else if clone.isRun() {
clone.runToArray()
clone = clone.runToArray()
}
if clone.n != cts[ct][exp].n {
t.Fatalf("test %s expected array n=%d, but got n=%d", desc, cts[ct][exp].n, clone.n)
if clone.N() != cts[ct][exp].N() {
t.Errorf("test %s expected array n=%d, but got n=%d", desc, cts[ct][exp].N(), clone.N())
}
// Because xorRunRun resulting in an empty container returns an array container with a
// nil slice array, then we need to check len() on array first (look for 0).
if !(len(clone.array()) == 0 && len(cts[ct][exp].array()) == 0) && !reflect.DeepEqual(clone.array(), cts[ct][exp].array()) {
t.Fatalf("test %s expected array %X, but got %X", desc, cts[ct][exp].array(), clone.array())
t.Errorf("test %s expected array %X, but got %X", desc, cts[ct][exp].array(), clone.array())
}
} else if ct == containerBitmap {
if clone.isArray() {
clone.arrayToBitmap()
if clone == nil {
clone = NewContainerBitmap(0, nil)
} else if clone.isArray() {
clone = clone.arrayToBitmap()
} else if clone.isRun() {
clone.runToBitmap()
clone = clone.runToBitmap()
}
if clone.n != cts[ct][exp].n {
t.Fatalf("test %s expected bitmap n=%d, but got n=%d", desc, cts[ct][exp].n, clone.n)
if clone.N() != cts[ct][exp].N() {
t.Errorf("test %s expected bitmap n=%d, but got n=%d", desc, cts[ct][exp].N(), clone.N())
}
if !reflect.DeepEqual(clone.bitmap(), cts[ct][exp].bitmap()) {
t.Fatalf("test %s expected bitmap %X, but got %X", desc, cts[ct][exp].bitmap(), clone.bitmap())
t.Errorf("test %s expected bitmap %X, but got %X", desc, cts[ct][exp].bitmap(), clone.bitmap())
}
} else if ct == containerRun {
if clone.isArray() {
clone.arrayToRun(0)
if clone == nil {
clone = NewContainerRun(nil)
} else if clone.isArray() {
clone = clone.arrayToRun(0)
} else if clone.isBitmap() {
clone.bitmapToRun(0)
clone = clone.bitmapToRun(0)
}
if clone.n != cts[ct][exp].n {
t.Fatalf("test %s expected runs n=%d, but got n=%d", desc, cts[ct][exp].n, clone.n)
if clone.N() != cts[ct][exp].N() {
t.Errorf("test %s expected runs n=%d, but got n=%d", desc, cts[ct][exp].N(), clone.N())
}
if !reflect.DeepEqual(clone.runs(), cts[ct][exp].runs()) {
t.Fatalf("test %s expected runs %X, but got %X", desc, cts[ct][exp].runs(), clone.runs())
t.Errorf("test %s expected runs %X, but got %X", desc, cts[ct][exp].runs(), clone.runs())
}
}
}
@ -3405,7 +3334,6 @@ func TestEquals(t *testing.T) {
}
*/
func TestShiftArray(t *testing.T) {
a := NewContainerArray(nil)
tests := []struct {
array []uint16
exp []uint16
@ -3429,10 +3357,16 @@ func TestShiftArray(t *testing.T) {
}
for i, test := range tests {
a.setArray(test.array)
a.n = int32(len(a.array()))
a := NewContainerArray(test.array)
ret1, _ := shift(a) // test generic shift function
ret2, _ := shiftArray(a) // test array-specific shift function
// accept nil *Container as valid substitute for empty array
if ret1 == nil {
ret1 = NewContainerArray(nil)
}
if ret2 == nil {
ret2 = NewContainerArray(nil)
}
if !reflect.DeepEqual(ret1.array(), test.exp) {
t.Fatalf("test #%v shift() expected %v, but got %v", i, test.exp, ret1.array())
} else if !reflect.DeepEqual(ret2.array(), test.exp) {
@ -3443,7 +3377,6 @@ func TestShiftArray(t *testing.T) {
func TestShiftBitmap(t *testing.T) {
// note, bitmaps are provided for us by the ensuing tests
a := NewContainerBitmap(0, nil)
tests := []struct {
bitmap []uint64
exp []uint64
@ -3463,11 +3396,10 @@ func TestShiftBitmap(t *testing.T) {
}
for i, test := range tests {
a.setBitmap(test.bitmap)
a.n = 1
a := NewContainerBitmap(-1, test.bitmap)
ret1, _ := shift(a) // test generic shift function
ret2, _ := shiftBitmap(a) // test bitmap-specific shift function
e := NewContainerBitmap(1, test.exp)
e := NewContainerBitmap(-1, test.exp)
if !reflect.DeepEqual(ret1.bitmap(), e.bitmap()) {
t.Fatalf("test #%v shift() expected %v, but got %v", i, e.bitmap(), ret1.bitmap())
} else if !reflect.DeepEqual(ret2.bitmap(), test.exp) {
@ -3476,8 +3408,6 @@ func TestShiftBitmap(t *testing.T) {
}
}
func TestShiftRun(t *testing.T) {
a := NewContainerRun(nil)
tests := []struct {
runs []interval16
n int32
@ -3509,14 +3439,13 @@ func TestShiftRun(t *testing.T) {
}
for i, test := range tests {
a.setRuns(test.runs)
a.n = test.n
a := NewContainerRun(test.runs)
ret1, c1 := shift(a) // test generic shift function
ret2, c2 := shiftRun(a) // test run-specific shift function
if !reflect.DeepEqual(ret1.runs(), test.exp) && c1 == test.carry && ret1.n == test.en {
t.Fatalf("test #%v shift() expected %v, but got %v %d", i, test.exp, ret1.runs(), ret1.n)
} else if !reflect.DeepEqual(ret2.runs(), test.exp) && c2 == test.carry && ret2.n == test.en {
t.Fatalf("test #%v shiftRun() expected %v, but got %v %d", i, test.exp, ret2.runs(), ret2.n)
if !reflect.DeepEqual(ret1.runs(), test.exp) && c1 == test.carry && ret1.N() == test.en {
t.Fatalf("test #%v shift() expected %v, but got %v %d", i, test.exp, ret1.runs(), ret1.N())
} else if !reflect.DeepEqual(ret2.runs(), test.exp) && c2 == test.carry && ret2.N() == test.en {
t.Fatalf("test #%v shiftRun() expected %v, but got %v %d", i, test.exp, ret2.runs(), ret2.N())
}
}
}

View file

@ -1113,14 +1113,15 @@ func testBitmapQuick(t *testing.T, n int, min, max uint64) {
}
// Remove all values in random order.
for _, i := range rand.Perm(len(a)) {
removed, _ := bm.Remove(a[i])
for i, item := range rand.Perm(len(a)) {
removed, _ := bm.Remove(a[item])
if removed {
manual_count--
}
//check count
if manual_count != bm.Count() {
t.Fatalf("expected bitmap Remove count to be: %d got: %d", manual_count, bm.Count())
t.Fatalf("removing %d/%d [%d] from bitmap: expected bitmap Remove count to be %d, got %d",
i, len(a), a[item], manual_count, bm.Count())
}
}

81
row.go
View file

@ -57,6 +57,12 @@ func (r *Row) IsEmpty() bool {
return true
}
func (r *Row) Freeze() {
for _, s := range r.segments {
s.Freeze()
}
}
// Merge merges data from other into r.
func (r *Row) Merge(other *Row) {
var segments []rowSegment
@ -210,15 +216,6 @@ func (r *Row) SetBit(i uint64) (changed bool) {
return r.createSegmentIfNotExists(i / ShardWidth).SetBit(i)
}
// clearBit clears the i-th column of the row.
func (r *Row) clearBit(i uint64) (changed bool) { // nolint: unparam
s := r.segment(i / ShardWidth)
if s == nil {
return false
}
return s.ClearBit(i)
}
// Segments returns a list of all segments in the row.
func (r *Row) Segments() []rowSegment {
return r.segments
@ -246,12 +243,12 @@ func (r *Row) createSegmentIfNotExists(shard uint64) *rowSegment {
}
// Insert new segment.
r.segments = append(r.segments, rowSegment{data: *roaring.NewBitmap()})
r.segments = append(r.segments, rowSegment{data: roaring.NewSliceBitmap()})
if i < len(r.segments) {
copy(r.segments[i+1:], r.segments[i:])
}
r.segments[i] = rowSegment{
data: *roaring.NewBitmap(),
data: roaring.NewSliceBitmap(),
shard: shard,
writable: true,
}
@ -312,13 +309,17 @@ type rowSegment struct {
// Underlying raw bitmap implementation.
// This is an mmapped bitmap if writable is false. Otherwise
// it is a heap allocated bitmap which can be manipulated.
data roaring.Bitmap
data *roaring.Bitmap
writable bool
// Bit count
n uint64
}
func (s *rowSegment) Freeze() {
s.data.Freeze()
}
// Merge adds chunks from other to s.
// Chunks in s are overwritten if they exist in other.
func (s *rowSegment) Merge(other *rowSegment) {
@ -332,50 +333,58 @@ func (s *rowSegment) Merge(other *rowSegment) {
// IntersectionCount returns the number of intersections between s and other.
func (s *rowSegment) IntersectionCount(other *rowSegment) uint64 {
return s.data.IntersectionCount(&other.data)
return s.data.IntersectionCount(other.data)
}
// Intersect returns the itersection of s and other.
func (s *rowSegment) Intersect(other *rowSegment) *rowSegment {
data := s.data.Intersect(&other.data)
data := s.data.Intersect(other.data)
data.Freeze()
return &rowSegment{
data: *data,
shard: s.shard,
n: data.Count(),
data: data,
shard: s.shard,
n: data.Count(),
writable: true,
}
}
// Union returns the bitwise union of s and other.
func (s *rowSegment) Union(other *rowSegment) *rowSegment {
data := s.data.Union(&other.data)
data := s.data.Union(other.data)
data.Freeze()
return &rowSegment{
data: *data,
shard: s.shard,
n: data.Count(),
data: data,
shard: s.shard,
n: data.Count(),
writable: true,
}
}
// Difference returns the diff of s and other.
func (s *rowSegment) Difference(other *rowSegment) *rowSegment {
data := s.data.Difference(&other.data)
data := s.data.Difference(other.data)
data.Freeze()
return &rowSegment{
data: *data,
shard: s.shard,
n: data.Count(),
data: data,
shard: s.shard,
n: data.Count(),
writable: true,
}
}
// Xor returns the xor of s and other.
func (s *rowSegment) Xor(other *rowSegment) *rowSegment {
data := s.data.Xor(&other.data)
data := s.data.Xor(other.data)
data.Freeze()
return &rowSegment{
data: *data,
shard: s.shard,
n: data.Count(),
data: data,
shard: s.shard,
n: data.Count(),
writable: true,
}
}
@ -386,11 +395,13 @@ func (s *rowSegment) Shift() (*rowSegment, error) {
if err != nil {
return nil, errors.Wrap(err, "shifting roaring data")
}
data.Freeze()
return &rowSegment{
data: *data,
shard: s.shard,
n: data.Count(),
data: data,
shard: s.shard,
n: data.Count(),
writable: true,
}, nil
}
@ -439,7 +450,11 @@ func (s *rowSegment) ensureWritable() {
return
}
s.data = *s.data.Clone()
// This doesn't actually clone all the containers, but does clone
// the bitmap itself -- we get a new bitmap, but it just marks the
// containers as frozen and shares them. It's now safe to write to
// this bitmap, but the actual containers are copy-on-write.
s.data = s.data.Freeze()
s.writable = true
}