mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Fixed conflicts; Merged with master
This commit is contained in:
commit
08f4ccb29b
17 changed files with 1456 additions and 1973 deletions
9
cache.go
9
cache.go
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
49
fragment.go
49
fragment.go
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
1016
roaring/roaring.go
1016
roaring/roaring.go
File diff suppressed because it is too large
Load diff
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
81
row.go
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue