drop enterprise/b

We added the containers_btree implementation to roaring/, which
makes it silly to keep this one. Also, this one is the only reason
that container.Mapped needed to be exported.
This commit is contained in:
Seebs 2019-05-13 12:36:03 -05:00
parent 8706dd990f
commit c587dbc94d
3 changed files with 0 additions and 1175 deletions

View file

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

View file

@ -1,212 +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) Last() (key uint64, c *roaring.Container) {
if btc.tree.Len() == 0 {
return 0, nil
}
k, v := btc.tree.Last()
return k, v
}
func (btc *bTreeContainers) Size() int {
return btc.tree.Len()
}
func (btc *bTreeContainers) Reset() {
btc.tree = treeNew(cmp)
btc.lastKey = 0
btc.lastContainer = nil
}
func (btc *bTreeContainers) Iterator(key uint64) (citer roaring.ContainerIterator, found bool) {
e, ok := btc.tree.Seek(key)
if ok {
found = true
}
return &btcIterator{
e: e,
}, found
}
func (btc *bTreeContainers) Repair() {
e, _ := btc.tree.Seek(0)
_, c, err := e.Next()
for err != io.EOF {
c.Repair()
_, c, err = e.Next()
}
}
type btcIterator struct {
e *enumerator
key uint64
val *roaring.Container
}
func (i *btcIterator) Next() bool {
k, v, err := i.e.Next()
if err == io.EOF {
return false
}
i.key = k
i.val = v
return true
}
func (i *btcIterator) Value() (uint64, *roaring.Container) {
if i.val == nil {
return 0, nil
}
return i.key, i.val
}

View file

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