mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-10-10 21:07:53 +00:00
Make containers copy-on-write
This patch replaces a lot of circumstances in which containers were being copied with circumstances in which they are shared, using copy-on-write semantics. To achieve this, we emulate somewhat the design of go's native `append` function. Operations on a container may optionally yield a new container. A container can be marked "frozen", after which no operation should ever write to it in any way; that applies both to the container itself and the backing store it refers to, if any. So for instance, instead of: c.arrayToBitmap() we now write: c = c.arrayToBitmap() Operations which need to modify a container in any way need to be able to return a new container, which is a modified copy of the previous container. This applies to operations like add/remove, but also to things like unmapping memory-mapped storage, or changing a container's type. Bitmaps do not support the same copy-on-write semantics, currently, but "copying" a bitmap and sharing the containers instead of duplicating them is *much* cheaper than copying the containers. Bitmaps do support a .Freeze method, which currently copies the previous bitmap, making a new one with the same container pointers, and freezes the individual containers. Use this if you need a writeable copy of a bitmap -- the resulting bitmap can safely have its set of containers modified, and bitmap operators that would want to modify the containers will use copy-on-write for that. The primary motivation of this is to reduce the cost of the row cache used by fragments. As a secondary issue, the row cache is no longer updated on writes -- that update was actually a race condition waiting to happen. Rather, writes to a row invalidate the cache entry for that row. The row cache is created by creating a new bitmap, and freezing the relevant containers from the fragment's storage. In the case where nothing is being written, the row cache grows to contain bitmaps containing all those containers, but never copies any containers. If nothing's being read, the row cache is never created, and the containers are in general not getting frozen. The only circumstance where copies have to happen is when things are read (and thus stored in the row cache) and later modified. In that case, each read freezes objects, and the first write to a container after it's been frozen will create a new copy. We drop the enterprise/b btree implementation, because we don't really need it anymore -- we now provide that implementation by default in the open source product anyway. Along with this, there's a lot of other changes which improve support for nil containers, as a cheaper representation for empty containers. Operations which we know will provide an empty container can always short-circuit and just yield a nil *Container. Similarly, operations which would provide a full container can return a single shared full container object (which is frozen). The higher-level (non type-specific) container ops are now using that logic to short-circuit operations for empty and full containers. (For instance, difference of anything minus an empty container is the original thing, union of anything and empty is the original thing, and so on.) The Containers interface adds "Update" and "UpdateEvery" methods, based in part on the "Put" interface provided by the underlying btree implementation; Update performs a possible update in-place of a container for a given key, bypassing the need to replicate the search for that key in the container. UpdateEvery loops through all the containers. Containers do not strictly guarantee that they won't return nil `*Container` objects. However, the container iterators won't return those -- empty containers aren't interesting. Some tests are updated to reflect this. Some of the container internals, like N(), or the isArray() and related functions, accept nil container pointers. Some, like Thaw(), do not. For the array(), bitmap(), and runs() methods, roaringparanoia enables an explicit panic on a nil container explaining the problem, but the intent is that those should never be called unless you already know you have the right kind of container, so by default they don't perform the extra checks. In most cases, this is already covered because a nil container is empty, and there's no operation we can perform that requires us to inspect the contents of an empty container. This is passing a fair amount of testing, but the testing may not be comprehensive enough. The overall impact of this is pretty trivial performance-wise. In our default roaring/ benchmarks, a few things get a few percent faster, or slower. The advantage is that, with read-heavy workloads, the row cache no longer eats up incredible amounts of memory. For a smallish test case, pilosa's memory usage (RES in top) after startup was ~2.5GB. Without this patch, simply reading every row a few times got memory usage to about 9GB, which seemed reasonably stable. With this patch, memory usage went to about 3GB. This will be less noticeable in mixed read/write loads, but it should be consistently significantly lower. In addition to dropping things from the rowCache on modifications, we also stopped performing a full count on a modified row when not using a cache of a kind that would use that count, and don't repopulate the rowCache regardless. We don't want every write to imply a corresponding read after it. There's a lot of room for possible future optimizations in terms of things like in-place operations, and some of the row/rowSegment code is a little suspicious to me, but I don't think it should be *worse* in any cases.
This commit is contained in:
parent
63120e3715
commit
c133ce0376
14 changed files with 1456 additions and 791 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.
|
||||
|
|
|
|||
49
fragment.go
49
fragment.go
|
|
@ -422,19 +422,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()
|
||||
|
|
@ -505,12 +504,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)
|
||||
|
||||
|
|
@ -566,12 +568,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)
|
||||
|
||||
|
|
@ -1849,9 +1854,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) Last() (key uint64, c *Container) {
|
||||
if btc.tree.Len() == 0 {
|
||||
return 0, nil
|
||||
|
|
@ -156,7 +178,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
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +205,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
|
||||
|
|
@ -187,19 +229,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) Last() (key uint64, c *Container) {
|
||||
if len(sc.keys) == 0 {
|
||||
return 0, nil
|
||||
|
|
@ -129,7 +146,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
|
||||
}
|
||||
|
|
@ -162,6 +179,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
|
||||
|
|
@ -170,14 +223,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) {
|
||||
|
|
|
|||
|
|
@ -43,14 +43,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() {
|
||||
|
|
@ -68,14 +68,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)
|
||||
|
|
@ -85,14 +85,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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1087,14 +1087,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