mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 00:55:55 +00:00
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.
1494 lines
26 KiB
Go
1494 lines
26 KiB
Go
// Copyright 2014 The b Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package roaring
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"math/rand"
|
|
"path"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strings"
|
|
"testing"
|
|
|
|
"modernc.org/mathutil"
|
|
"modernc.org/strutil"
|
|
)
|
|
|
|
var caller = func(s string, va ...interface{}) {
|
|
_, fn, fl, _ := runtime.Caller(2)
|
|
fmt.Printf("%s:%d: ", path.Base(fn), fl)
|
|
fmt.Printf(s, va...)
|
|
fmt.Println()
|
|
}
|
|
|
|
func dbg(s string, va ...interface{}) {
|
|
if s == "" {
|
|
s = strings.Repeat("%v ", len(va))
|
|
}
|
|
_, fn, fl, _ := runtime.Caller(1)
|
|
fmt.Printf("%s:%d: ", path.Base(fn), fl)
|
|
fmt.Printf(s, va...)
|
|
fmt.Println()
|
|
}
|
|
|
|
func TODO(...interface{}) string { //TODOOK
|
|
_, fn, fl, _ := runtime.Caller(1)
|
|
return fmt.Sprintf("TODO: %s:%d:\n", path.Base(fn), fl) //TODOOK
|
|
}
|
|
|
|
func use(...interface{}) {}
|
|
|
|
func init() { // nolint: gochecknoinits
|
|
use(caller, dbg, TODO, isNil, (*tree).dump) //TODOOK
|
|
}
|
|
|
|
// ============================================================================
|
|
|
|
func isNil(p interface{}) bool {
|
|
switch x := p.(type) {
|
|
case *x:
|
|
if x == nil {
|
|
return true
|
|
}
|
|
case *d:
|
|
if x == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *tree) dump() string {
|
|
var buf bytes.Buffer
|
|
f := strutil.IndentFormatter(&buf, "\t")
|
|
|
|
num := map[interface{}]int{}
|
|
visited := map[interface{}]bool{}
|
|
|
|
handle := func(p interface{}) int {
|
|
if isNil(p) {
|
|
return 0
|
|
}
|
|
|
|
if n, ok := num[p]; ok {
|
|
return n
|
|
}
|
|
|
|
n := len(num) + 1
|
|
num[p] = n
|
|
return n
|
|
}
|
|
|
|
var pagedump func(interface{}, string)
|
|
pagedump = func(p interface{}, pref string) {
|
|
if isNil(p) || visited[p] {
|
|
return
|
|
}
|
|
|
|
visited[p] = true
|
|
switch x := p.(type) {
|
|
case *x:
|
|
h := handle(p)
|
|
n := 0
|
|
for i, v := range x.x {
|
|
if v.ch != nil || v.k != 0 {
|
|
n = i + 1
|
|
}
|
|
}
|
|
_, _ = f.Format("%sX#%d(%p) n %d:%d {", pref, h, x, x.c, n)
|
|
a := []interface{}{}
|
|
for i, v := range x.x[:n] {
|
|
a = append(a, v.ch)
|
|
if i != 0 {
|
|
_, _ = f.Format(" ")
|
|
}
|
|
_, _ = f.Format("(C#%d K %v)", handle(v.ch), v.k)
|
|
}
|
|
_, _ = f.Format("}\n")
|
|
for _, p := range a {
|
|
pagedump(p, pref+". ")
|
|
}
|
|
case *d:
|
|
h := handle(p)
|
|
n := 0
|
|
for i, d := range x.d {
|
|
if d.v != nil {
|
|
n = i + 1
|
|
}
|
|
}
|
|
_, _ = f.Format("%sD#%d(%p) P#%d N#%d n %d:%d {", pref, h, x, handle(x.p), handle(x.n), x.c, n)
|
|
for i, d := range x.d[:n] {
|
|
if i != 0 {
|
|
_, _ = f.Format(" ")
|
|
}
|
|
_, _ = f.Format("%v:%v", d.k, d.v)
|
|
}
|
|
_, _ = f.Format("}\n")
|
|
}
|
|
}
|
|
|
|
pagedump(t.r, "")
|
|
s := buf.String()
|
|
if s != "" {
|
|
s = s[:len(s)-1]
|
|
}
|
|
return s
|
|
}
|
|
|
|
func rng() *mathutil.FC32 {
|
|
x, err := mathutil.NewFC32(math.MinInt32/4, math.MaxInt32/4, false)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return x
|
|
}
|
|
|
|
var dummyC [1000]Container
|
|
|
|
func getDummyC(i int) *Container {
|
|
if i < 0 {
|
|
i *= -1
|
|
}
|
|
return &dummyC[i%len(dummyC)]
|
|
}
|
|
|
|
func copyBenchmark(fn func(int64) uint64) func(b *testing.B) {
|
|
return func(b *testing.B) {
|
|
r := treeNew()
|
|
for i := int64(0); i < int64(b.N); i++ {
|
|
j := fn(i)
|
|
r.Set(j, getDummyC(0))
|
|
}
|
|
b.Logf("copied %d de for N=%d\n", r.countCopies(), b.N)
|
|
}
|
|
}
|
|
|
|
func BenchmarkBtreeDeCopies(b *testing.B) {
|
|
b.Run("Linear", copyBenchmark(func(i int64) uint64 { return uint64(i) }))
|
|
b.Run("Random", copyBenchmark(func(i int64) uint64 { return rand.Uint64() }))
|
|
}
|
|
|
|
func TestBtreeGet0(t *testing.T) {
|
|
r := treeNew()
|
|
if g, e := r.Len(), 0; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
_, ok := r.Get(42)
|
|
if ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
}
|
|
|
|
func TestBtreeSetGet0(t *testing.T) {
|
|
r := treeNew()
|
|
set := r.Set
|
|
set(42, getDummyC(0))
|
|
if g, e := r.Len(), 1; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
v, ok := r.Get(42)
|
|
if !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC(0); g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
set(42, getDummyC(1))
|
|
if g, e := r.Len(), 1; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
v, ok = r.Get(42)
|
|
if !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC(1); g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
set(420, getDummyC(2))
|
|
if g, e := r.Len(), 2; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
v, ok = r.Get(42)
|
|
if !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC(1); g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
v, ok = r.Get(420)
|
|
if !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC(2); g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSetGet1(t *testing.T) {
|
|
const N = 40000
|
|
for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} {
|
|
r := treeNew()
|
|
set := r.Set
|
|
a := make([]int, N)
|
|
for i := range a {
|
|
a[i] = (i ^ x) << 1
|
|
}
|
|
for i, k := range a {
|
|
set(uint64(k), getDummyC((k^x)%10))
|
|
if g, e := r.Len(), i+1; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
}
|
|
|
|
for i, k := range a {
|
|
v, ok := r.Get(uint64(k))
|
|
if !ok {
|
|
t.Fatal(i, k, v, ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC((k^x)%10); g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
k |= 1
|
|
_, ok = r.Get(uint64(k))
|
|
if ok {
|
|
t.Fatal(i, k)
|
|
}
|
|
|
|
}
|
|
|
|
for _, k := range a {
|
|
r.Set(uint64(k), getDummyC((k^x)+42))
|
|
}
|
|
|
|
for i, k := range a {
|
|
v, ok := r.Get(uint64(k))
|
|
if !ok {
|
|
t.Fatal(i, k, v, ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC((k^x)+42); g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
k |= 1
|
|
_, ok = r.Get(uint64(k))
|
|
if ok {
|
|
t.Fatal(i, k)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreePrealloc(*testing.T) {
|
|
const n = 2e6
|
|
rng := rng()
|
|
a := make([]int, n)
|
|
for i := range a {
|
|
a[i] = rng.Next()
|
|
}
|
|
r := treeNew()
|
|
for _, v := range a {
|
|
r.Set(uint64(v), nil)
|
|
}
|
|
r.Close()
|
|
}
|
|
|
|
func TestBtreeSplitXOnEdge(t *testing.T) {
|
|
// verify how splitX works when splitting X for k pointing directly at split edge
|
|
tr := treeNew()
|
|
|
|
set := tr.Set
|
|
// one index page with 2*kx+2 elements (last has .k=∞ so x.c=2*kx+1)
|
|
// which will splitX on next Set
|
|
for i := 0; i <= (2*kx+1)*2*kd; i++ {
|
|
// odd keys are left to be filled in second test
|
|
set(uint64(2*i), getDummyC(2*i))
|
|
}
|
|
|
|
x0 := tr.r.(*x)
|
|
if x0.c != 2*kx+1 {
|
|
t.Fatalf("x0.c: %v ; expected %v", x0.c, 2*kx+1)
|
|
}
|
|
|
|
// set element with k directly at x0[kx].k
|
|
kedge := uint64(2 * (kx + 1) * (2 * kd))
|
|
if x0.x[kx].k != kedge {
|
|
t.Fatalf("edge key before splitX: %v ; expected %v", x0.x[kx].k, kedge)
|
|
}
|
|
set(uint64(kedge), getDummyC(777))
|
|
|
|
// if splitX was wrong kedge:777 would land into wrong place with Get failing
|
|
v, ok := tr.Get(kedge)
|
|
if !(v == getDummyC(777) && ok) {
|
|
t.Fatalf("after splitX: Get(%v) -> %v, %v ; expected 777, true", kedge, v, ok)
|
|
}
|
|
|
|
// now check the same when splitted X has parent
|
|
xr := tr.r.(*x)
|
|
if xr.c != 1 { // second x comes with k=∞ with .c index
|
|
t.Fatalf("after splitX: xr.c: %v ; expected 1", xr.c)
|
|
}
|
|
|
|
if xr.x[0].ch != x0 {
|
|
t.Fatal("xr[0].ch is not x0")
|
|
}
|
|
|
|
for i := 0; i <= (2*kx)*kd; i++ {
|
|
set(uint64(2*i+1), getDummyC(2*i+1))
|
|
}
|
|
|
|
// check x0 is in pre-splitX condition and still at the right place
|
|
if x0.c != 2*kx+1 {
|
|
t.Fatalf("x0.c: %v ; expected %v", x0.c, 2*kx+1)
|
|
}
|
|
if xr.x[0].ch != x0 {
|
|
t.Fatal("xr[0].ch is not x0")
|
|
}
|
|
|
|
// set element with k directly at x0[kx].k
|
|
kedge = (kx + 1) * (2 * kd)
|
|
if x0.x[kx].k != kedge {
|
|
t.Fatalf("edge key before splitX: %v ; expected %v", x0.x[kx].k, kedge)
|
|
}
|
|
set(uint64(kedge), getDummyC(888))
|
|
|
|
// if splitX was wrong kedge:888 would land into wrong place
|
|
v, ok = tr.Get(kedge)
|
|
if !(v == getDummyC(888) && ok) {
|
|
t.Fatalf("after splitX: Get(%v) -> %v, %v ; expected 888, true", kedge, v, ok)
|
|
}
|
|
}
|
|
|
|
func BenchmarkBtreeSetSeq1e3(b *testing.B) {
|
|
benchmarkSetSeq(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeSetSeq1e4(b *testing.B) {
|
|
benchmarkSetSeq(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeSetSeq1e5(b *testing.B) {
|
|
benchmarkSetSeq(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeSetSeq1e6(b *testing.B) {
|
|
benchmarkSetSeq(b, 1e6)
|
|
}
|
|
|
|
func benchmarkSetSeq(b *testing.B, n int) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
r := treeNew()
|
|
debug.FreeOSMemory()
|
|
b.StartTimer()
|
|
for j := 0; j < n; j++ {
|
|
r.Set(uint64(j), getDummyC(j))
|
|
}
|
|
b.StopTimer()
|
|
r.Close()
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func BenchmarkBtreeGetSeq1e3(b *testing.B) {
|
|
benchmarkGetSeq(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeGetSeq1e4(b *testing.B) {
|
|
benchmarkGetSeq(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeGetSeq1e5(b *testing.B) {
|
|
benchmarkGetSeq(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeGetSeq1e6(b *testing.B) {
|
|
benchmarkGetSeq(b, 1e6)
|
|
}
|
|
|
|
func benchmarkGetSeq(b *testing.B, n int) {
|
|
r := treeNew()
|
|
for i := 0; i < n; i++ {
|
|
r.Set(uint64(i), getDummyC(i))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for j := 0; j < n; j++ {
|
|
r.Get(uint64(j))
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
r.Close()
|
|
}
|
|
|
|
func BenchmarkBtreeSetRnd1e3(b *testing.B) {
|
|
benchmarkSetRnd(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeSetRnd1e4(b *testing.B) {
|
|
benchmarkSetRnd(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeSetRnd1e5(b *testing.B) {
|
|
benchmarkSetRnd(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeSetRnd1e6(b *testing.B) {
|
|
benchmarkSetRnd(b, 1e6)
|
|
}
|
|
|
|
func benchmarkSetRnd(b *testing.B, n int) {
|
|
rng := rng()
|
|
a := make([]int, n)
|
|
for i := range a {
|
|
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), c)
|
|
}
|
|
b.StopTimer()
|
|
r.Close()
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func BenchmarkBtreeGetRnd1e3(b *testing.B) {
|
|
benchmarkGetRnd(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeGetRnd1e4(b *testing.B) {
|
|
benchmarkGetRnd(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeGetRnd1e5(b *testing.B) {
|
|
benchmarkGetRnd(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeGetRnd1e6(b *testing.B) {
|
|
benchmarkGetRnd(b, 1e6)
|
|
}
|
|
|
|
func benchmarkGetRnd(b *testing.B, n int) {
|
|
r := treeNew()
|
|
rng := rng()
|
|
a := make([]int, n)
|
|
for i := range a {
|
|
a[i] = rng.Next()
|
|
}
|
|
c := getDummyC(1)
|
|
for _, v := range a {
|
|
r.Set(uint64(v), c)
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for _, v := range a {
|
|
r.Get(uint64(v))
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
r.Close()
|
|
}
|
|
|
|
func TestBtreeSetGet2(t *testing.T) {
|
|
const N = 40000
|
|
for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} {
|
|
rng := rng()
|
|
r := treeNew()
|
|
set := r.Set
|
|
a := make([]int, N)
|
|
for i := range a {
|
|
a[i] = (rng.Next() ^ x) << 1
|
|
}
|
|
for i, k := range a {
|
|
set(uint64(k), getDummyC(k^x))
|
|
if g, e := r.Len(), i+1; g != e {
|
|
t.Fatal(i, x, g, e)
|
|
}
|
|
}
|
|
|
|
for i, k := range a {
|
|
v, ok := r.Get(uint64(k))
|
|
if !ok {
|
|
t.Fatal(i, k, v, ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC(k^x); g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
k |= 1
|
|
_, ok = r.Get(uint64(k))
|
|
if ok {
|
|
t.Fatal(i, k)
|
|
}
|
|
}
|
|
|
|
for _, k := range a {
|
|
r.Set(uint64(k), getDummyC((k^x)+42))
|
|
}
|
|
|
|
for i, k := range a {
|
|
v, ok := r.Get(uint64(k))
|
|
if !ok {
|
|
t.Fatal(i, k, v, ok)
|
|
}
|
|
|
|
if g, e := v, getDummyC(k^x+42); g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
k |= 1
|
|
_, ok = r.Get(uint64(k))
|
|
if ok {
|
|
t.Fatal(i, k)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreeSetGet3(t *testing.T) {
|
|
r := treeNew()
|
|
set := r.Set
|
|
var i int
|
|
for i = 0; ; i++ {
|
|
set(uint64(i), getDummyC(-i))
|
|
if _, ok := r.r.(*x); ok {
|
|
break
|
|
}
|
|
}
|
|
for j := 0; j <= i; j++ {
|
|
set(uint64(j), getDummyC(j))
|
|
}
|
|
|
|
for j := 0; j <= i; j++ {
|
|
v, ok := r.Get(uint64(j))
|
|
if !ok {
|
|
t.Fatal(j)
|
|
}
|
|
|
|
if g, e := v, getDummyC(j); g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreeDelete0(t *testing.T) {
|
|
r := treeNew()
|
|
if ok := r.Delete(0); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 0; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
r.Set(uint64(0), getDummyC(0))
|
|
if ok := r.Delete(1); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 1; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
if ok := r.Delete(0); !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 0; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
if ok := r.Delete(0); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
r.Set(uint64(0), getDummyC(0))
|
|
r.Set(uint64(1), getDummyC(1))
|
|
if ok := r.Delete(1); !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 1; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
if ok := r.Delete(1); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if ok := r.Delete(0); !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 0; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
if ok := r.Delete(0); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
r.Set(uint64(0), getDummyC(0))
|
|
r.Set(uint64(1), getDummyC(1))
|
|
if ok := r.Delete(0); !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 1; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
if ok := r.Delete(0); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if ok := r.Delete(1); !ok {
|
|
t.Fatal(ok)
|
|
}
|
|
|
|
if g, e := r.Len(), 0; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
|
|
if ok := r.Delete(1); ok {
|
|
t.Fatal(ok)
|
|
}
|
|
}
|
|
|
|
func TestBtreeDelete1(t *testing.T) {
|
|
const N = 130000
|
|
for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} {
|
|
r := treeNew()
|
|
set := r.Set
|
|
a := make([]int, N)
|
|
for i := range a {
|
|
a[i] = (i ^ x) << 1
|
|
}
|
|
for _, k := range a {
|
|
set(uint64(k), getDummyC(0))
|
|
}
|
|
|
|
for i, k := range a {
|
|
ok := r.Delete(uint64(k))
|
|
if !ok {
|
|
t.Fatal(i, x, k)
|
|
}
|
|
|
|
if g, e := r.Len(), N-i-1; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkBtreeDelSeq1e3(b *testing.B) {
|
|
benchmarkDelSeq(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeDelSeq1e4(b *testing.B) {
|
|
benchmarkDelSeq(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeDelSeq1e5(b *testing.B) {
|
|
benchmarkDelSeq(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeDelSeq1e6(b *testing.B) {
|
|
benchmarkDelSeq(b, 1e6)
|
|
}
|
|
|
|
func benchmarkDelSeq(b *testing.B, n int) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
r := treeNew()
|
|
for i := 0; i < n; i++ {
|
|
r.Set(uint64(i), getDummyC(i))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.StartTimer()
|
|
for j := 0; j < n; j++ {
|
|
r.Delete(uint64(j))
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func BenchmarkBtreeDelRnd1e3(b *testing.B) {
|
|
benchmarkDelRnd(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeDelRnd1e4(b *testing.B) {
|
|
benchmarkDelRnd(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeDelRnd1e5(b *testing.B) {
|
|
benchmarkDelRnd(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeDelRnd1e6(b *testing.B) {
|
|
benchmarkDelRnd(b, 1e6)
|
|
}
|
|
|
|
func benchmarkDelRnd(b *testing.B, n int) {
|
|
rng := rng()
|
|
a := make([]int, n)
|
|
for i := range a {
|
|
a[i] = rng.Next()
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
r := treeNew()
|
|
for _, v := range a {
|
|
r.Set(uint64(v), getDummyC(0))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.StartTimer()
|
|
for _, v := range a {
|
|
r.Delete(uint64(v))
|
|
}
|
|
b.StopTimer()
|
|
r.Close()
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func TestBtreeDelete2(t *testing.T) {
|
|
const N = 100000
|
|
for _, x := range []int{0, -1, 0x555555, 0xaaaaaa, 0x333333, 0xcccccc, 0x314159} {
|
|
r := treeNew()
|
|
set := r.Set
|
|
a := make([]int, N)
|
|
rng := rng()
|
|
for i := range a {
|
|
a[i] = (rng.Next() ^ x) << 1
|
|
}
|
|
for _, k := range a {
|
|
set(uint64(k), getDummyC(0))
|
|
}
|
|
|
|
for i, k := range a {
|
|
ok := r.Delete(uint64(k))
|
|
if !ok {
|
|
t.Fatal(i, x, k)
|
|
}
|
|
|
|
if g, e := r.Len(), N-i-1; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreeEnumeratorNext(t *testing.T) {
|
|
// seeking within 3 keys: 10, 20, 30
|
|
table := []struct {
|
|
k int
|
|
hit bool
|
|
keys []int
|
|
}{
|
|
{5, false, []int{10, 20, 30}},
|
|
{10, true, []int{10, 20, 30}},
|
|
{15, false, []int{20, 30}},
|
|
{20, true, []int{20, 30}},
|
|
{25, false, []int{30}},
|
|
{30, true, []int{30}},
|
|
{35, false, []int{}},
|
|
}
|
|
|
|
for i, test := range table {
|
|
up := test.keys
|
|
r := treeNew()
|
|
|
|
r.Set(uint64(10), getDummyC(100))
|
|
r.Set(uint64(20), getDummyC(200))
|
|
r.Set(uint64(30), getDummyC(300))
|
|
|
|
for verChange := 0; verChange < 16; verChange++ {
|
|
en, hit := r.Seek(uint64(test.k))
|
|
|
|
if g, e := hit, test.hit; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
j := 0
|
|
for {
|
|
if verChange&(1<<uint(j)) != 0 {
|
|
r.Set(uint64(20), getDummyC(200))
|
|
}
|
|
|
|
k, v, err := en.Next()
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
t.Fatal(i, err)
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
if j >= len(up) {
|
|
t.Fatal(i, j, verChange)
|
|
}
|
|
|
|
if g, e := k, uint64(up[j]); g != e {
|
|
t.Fatal(i, j, verChange, g, e)
|
|
}
|
|
|
|
if g, e := v, getDummyC(10*up[j]); g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
if g, e := j, len(up); g != e {
|
|
t.Fatal(i, j, g, e)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestBtreeEnumeratorPrev(t *testing.T) {
|
|
// seeking within 3 keys: 10, 20, 30
|
|
table := []struct {
|
|
k int
|
|
hit bool
|
|
keys []int
|
|
}{
|
|
{5, false, []int{}},
|
|
{10, true, []int{10}},
|
|
{15, false, []int{10}},
|
|
{20, true, []int{20, 10}},
|
|
{25, false, []int{20, 10}},
|
|
{30, true, []int{30, 20, 10}},
|
|
{35, false, []int{30, 20, 10}},
|
|
}
|
|
|
|
for i, test := range table {
|
|
dn := test.keys
|
|
r := treeNew()
|
|
|
|
r.Set(uint64(10), getDummyC(100))
|
|
r.Set(uint64(20), getDummyC(200))
|
|
r.Set(uint64(30), getDummyC(300))
|
|
|
|
for verChange := 0; verChange < 16; verChange++ {
|
|
en, hit := r.Seek(uint64(test.k))
|
|
|
|
if g, e := hit, test.hit; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
j := 0
|
|
for {
|
|
if verChange&(1<<uint(j)) != 0 {
|
|
r.Set(uint64(20), getDummyC(200))
|
|
}
|
|
|
|
k, v, err := en.Prev()
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
t.Fatal(i, err)
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
if j >= len(dn) {
|
|
t.Fatal(i, j, verChange)
|
|
}
|
|
|
|
if g, e := k, uint64(dn[j]); g != e {
|
|
t.Fatal(i, j, verChange, g, e)
|
|
}
|
|
|
|
if g, e := v, getDummyC(10*dn[j]); g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
if g, e := j, len(dn); g != e {
|
|
t.Fatal(i, j, g, e)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestBtreeEnumeratorPrevSanity(t *testing.T) {
|
|
// seeking within 3 keys: 10, 20, 30
|
|
table := []struct {
|
|
k int
|
|
hit bool
|
|
keyOut uint64
|
|
valueOut *Container
|
|
errorOut error
|
|
}{
|
|
{10, true, 10, getDummyC(100), nil},
|
|
{20, true, 20, getDummyC(200), nil},
|
|
{30, true, 30, getDummyC(300), nil},
|
|
{35, false, 30, getDummyC(300), nil},
|
|
{25, false, 20, getDummyC(200), nil},
|
|
{15, false, 10, getDummyC(100), nil},
|
|
{5, false, 0, nil, io.EOF},
|
|
}
|
|
|
|
for i, test := range table {
|
|
r := treeNew()
|
|
|
|
r.Set(uint64(10), getDummyC(100))
|
|
r.Set(uint64(20), getDummyC(200))
|
|
r.Set(uint64(30), getDummyC(300))
|
|
|
|
en, hit := r.Seek(uint64(test.k))
|
|
|
|
if g, e := hit, test.hit; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
|
|
k, v, err := en.Prev()
|
|
|
|
if g, e := err, test.errorOut; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
if g, e := k, test.keyOut; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
if g, e := v, test.valueOut; g != e {
|
|
t.Fatal(i, g, e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkBtreeSeekSeq1e3(b *testing.B) {
|
|
benchmarkSeekSeq(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeSeekSeq1e4(b *testing.B) {
|
|
benchmarkSeekSeq(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeSeekSeq1e5(b *testing.B) {
|
|
benchmarkSeekSeq(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeSeekSeq1e6(b *testing.B) {
|
|
benchmarkSeekSeq(b, 1e6)
|
|
}
|
|
|
|
func benchmarkSeekSeq(b *testing.B, n int) {
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
t := treeNew()
|
|
for j := 0; j < n; j++ {
|
|
t.Set(uint64(j), getDummyC(0))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.StartTimer()
|
|
for j := 0; j < n; j++ {
|
|
e, _ := t.Seek(uint64(j))
|
|
e.Close()
|
|
}
|
|
b.StopTimer()
|
|
t.Close()
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func BenchmarkBtreeSeekRnd1e3(b *testing.B) {
|
|
benchmarkSeekRnd(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeSeekRnd1e4(b *testing.B) {
|
|
benchmarkSeekRnd(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeSeekRnd1e5(b *testing.B) {
|
|
benchmarkSeekRnd(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeSeekRnd1e6(b *testing.B) {
|
|
benchmarkSeekRnd(b, 1e6)
|
|
}
|
|
|
|
func benchmarkSeekRnd(b *testing.B, n int) {
|
|
r := treeNew()
|
|
rng := rng()
|
|
a := make([]int, n)
|
|
for i := range a {
|
|
a[i] = rng.Next()
|
|
}
|
|
for _, v := range a {
|
|
r.Set(uint64(v), getDummyC(0))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for _, v := range a {
|
|
e, _ := r.Seek(uint64(v))
|
|
e.Close()
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
r.Close()
|
|
}
|
|
|
|
func BenchmarkBtreeNext1e3(b *testing.B) {
|
|
benchmarkNext(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreeNext1e4(b *testing.B) {
|
|
benchmarkNext(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreeNext1e5(b *testing.B) {
|
|
benchmarkNext(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreeNext1e6(b *testing.B) {
|
|
benchmarkNext(b, 1e6)
|
|
}
|
|
|
|
func benchmarkNext(b *testing.B, n int) {
|
|
t := treeNew()
|
|
for i := 0; i < n; i++ {
|
|
t.Set(uint64(i), getDummyC(0))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
en, err := t.SeekFirst()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
m := 0
|
|
for {
|
|
if _, _, err = en.Next(); err != nil {
|
|
break
|
|
}
|
|
m++
|
|
}
|
|
if m != n {
|
|
b.Fatal(m)
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
t.Close()
|
|
}
|
|
|
|
func BenchmarkBtreePrev1e3(b *testing.B) {
|
|
benchmarkPrev(b, 1e3)
|
|
}
|
|
|
|
func BenchmarkBtreePrev1e4(b *testing.B) {
|
|
benchmarkPrev(b, 1e4)
|
|
}
|
|
|
|
func BenchmarkBtreePrev1e5(b *testing.B) {
|
|
benchmarkPrev(b, 1e5)
|
|
}
|
|
|
|
func BenchmarkBtreePrev1e6(b *testing.B) {
|
|
benchmarkPrev(b, 1e6)
|
|
}
|
|
|
|
func benchmarkPrev(b *testing.B, n int) {
|
|
t := treeNew()
|
|
for i := 0; i < n; i++ {
|
|
t.Set(uint64(i), getDummyC(0))
|
|
}
|
|
debug.FreeOSMemory()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
en, err := t.SeekLast()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
m := 0
|
|
for {
|
|
if _, _, err = en.Prev(); err != nil {
|
|
break
|
|
}
|
|
m++
|
|
}
|
|
if m != n {
|
|
b.Fatal(m)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekFirst0(t *testing.T) {
|
|
b := treeNew()
|
|
_, err := b.SeekFirst()
|
|
if g, e := err, io.EOF; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekFirst1(t *testing.T) {
|
|
b := treeNew()
|
|
b.Set(uint64(1), getDummyC(10))
|
|
en, err := b.SeekFirst()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
k, v, err := en.Next()
|
|
if k != 1 || v != getDummyC(10) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Next()
|
|
if err == nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekFirst2(t *testing.T) {
|
|
b := treeNew()
|
|
b.Set(uint64(1), getDummyC(10))
|
|
b.Set(uint64(2), getDummyC(20))
|
|
en, err := b.SeekFirst()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
k, v, err := en.Next()
|
|
if k != 1 || v != getDummyC(10) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Next()
|
|
if k != 2 || v != getDummyC(20) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Next()
|
|
if err == nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekFirst3(t *testing.T) {
|
|
b := treeNew()
|
|
b.Set(uint64(2), getDummyC(20))
|
|
b.Set(uint64(3), getDummyC(30))
|
|
b.Set(uint64(1), getDummyC(10))
|
|
en, err := b.SeekFirst()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
k, v, err := en.Next()
|
|
if k != 1 || v != getDummyC(10) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Next()
|
|
if k != 2 || v != getDummyC(20) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Next()
|
|
if k != 3 || v != getDummyC(30) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Next()
|
|
if err == nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekLast0(t *testing.T) {
|
|
b := treeNew()
|
|
_, err := b.SeekLast()
|
|
if g, e := err, io.EOF; g != e {
|
|
t.Fatal(g, e)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekLast1(t *testing.T) {
|
|
b := treeNew()
|
|
b.Set(uint64(1), getDummyC(10))
|
|
en, err := b.SeekLast()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
k, v, err := en.Prev()
|
|
if k != 1 || v != getDummyC(10) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Prev()
|
|
if err == nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekLast2(t *testing.T) {
|
|
b := treeNew()
|
|
b.Set(uint64(1), getDummyC(10))
|
|
b.Set(uint64(2), getDummyC(20))
|
|
en, err := b.SeekLast()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
k, v, err := en.Prev()
|
|
if k != 2 || v != getDummyC(20) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Prev()
|
|
if k != 1 || v != getDummyC(10) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Prev()
|
|
if err == nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeekLast3(t *testing.T) {
|
|
b := treeNew()
|
|
b.Set(uint64(2), getDummyC(20))
|
|
b.Set(uint64(3), getDummyC(30))
|
|
b.Set(uint64(1), getDummyC(10))
|
|
en, err := b.SeekLast()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
k, v, err := en.Prev()
|
|
if k != 3 || v != getDummyC(30) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Prev()
|
|
if k != 2 || v != getDummyC(20) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Prev()
|
|
if k != 1 || v != getDummyC(10) || err != nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
|
|
k, v, err = en.Prev()
|
|
if err == nil {
|
|
t.Fatal(k, v, err)
|
|
}
|
|
}
|
|
|
|
func TestBtreePut(t *testing.T) {
|
|
tab := []struct {
|
|
pre []int // even index: K, odd index: V
|
|
newK int // Put(newK, ...
|
|
oldV int // Put()->oldV
|
|
exists bool // upd(exists)
|
|
write bool // upd()->write
|
|
post []int // even index: K, odd index: V
|
|
}{
|
|
// 0
|
|
{
|
|
[]int{},
|
|
1, 0, false, false,
|
|
[]int{},
|
|
},
|
|
{
|
|
[]int{},
|
|
1, 0, false, true,
|
|
[]int{1, -1},
|
|
},
|
|
{
|
|
[]int{1, 10},
|
|
0, 0, false, false,
|
|
[]int{1, 10},
|
|
},
|
|
{
|
|
[]int{1, 10},
|
|
0, 0, false, true,
|
|
[]int{0, -1, 1, 10},
|
|
},
|
|
{
|
|
[]int{1, 10},
|
|
1, 10, true, false,
|
|
[]int{1, 10},
|
|
},
|
|
|
|
// 5
|
|
{
|
|
[]int{1, 10},
|
|
1, 10, true, true,
|
|
[]int{1, -1},
|
|
},
|
|
{
|
|
[]int{1, 10},
|
|
2, 0, false, false,
|
|
[]int{1, 10},
|
|
},
|
|
{
|
|
[]int{1, 10},
|
|
2, 0, false, true,
|
|
[]int{1, 10, 2, -1},
|
|
},
|
|
}
|
|
|
|
for iTest, test := range tab {
|
|
tr := treeNew()
|
|
for i := 0; i < len(test.pre); i += 2 {
|
|
k, v := test.pre[i], test.pre[i+1]
|
|
tr.Set(uint64(k), getDummyC(v))
|
|
}
|
|
|
|
oldV, written := tr.Put(uint64(test.newK), func(old *Container, exists bool) (newV *Container, write bool) {
|
|
if g, e := exists, test.exists; g != e {
|
|
t.Fatal(iTest, g, e)
|
|
}
|
|
|
|
if exists {
|
|
if g, e := old, getDummyC(test.oldV); g != e {
|
|
t.Fatal(iTest, g, e)
|
|
}
|
|
}
|
|
return getDummyC(99), test.write
|
|
})
|
|
if test.exists {
|
|
if g, e := oldV, getDummyC(test.oldV); g != e {
|
|
t.Fatal(iTest, g, e)
|
|
}
|
|
}
|
|
|
|
if g, e := written, test.write; g != e {
|
|
t.Fatal(iTest, g, e)
|
|
}
|
|
|
|
n := len(test.post)
|
|
en, err := tr.SeekFirst()
|
|
if err != nil {
|
|
if n == 0 && err == io.EOF {
|
|
continue
|
|
}
|
|
|
|
t.Fatal(iTest, err)
|
|
}
|
|
|
|
for i := 0; i < len(test.post); i += 2 {
|
|
k, v, err := en.Next()
|
|
if err != nil {
|
|
t.Fatal(iTest, err)
|
|
}
|
|
|
|
if g, e := k, uint64(test.post[i]); g != e {
|
|
t.Fatal(iTest, g, e)
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
_, _, err = en.Next()
|
|
if g, e := err, io.EOF; g != e {
|
|
t.Fatal(iTest, g, e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreeSeek(t *testing.T) {
|
|
const N = 1 << 13
|
|
tr := treeNew()
|
|
for i := 0; i < N; i++ {
|
|
k := 2*i + 1
|
|
tr.Set(uint64(k), getDummyC(1))
|
|
}
|
|
for i := 0; i < N; i++ {
|
|
k := 2 * i
|
|
e, ok := tr.Seek(uint64(k))
|
|
if ok {
|
|
t.Fatal(k)
|
|
}
|
|
|
|
for j := i; j < N; j++ {
|
|
k2, _, err := e.Next()
|
|
if err != nil {
|
|
t.Fatal(k, err)
|
|
}
|
|
|
|
if g, e := k2, uint64(2*j+1); g != e {
|
|
t.Fatal(j, g, e)
|
|
}
|
|
}
|
|
|
|
_, _, err := e.Next()
|
|
if err != io.EOF {
|
|
t.Fatalf("expected io.EOF, got %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBtreePR4(t *testing.T) {
|
|
tr := treeNew()
|
|
for i := 0; i < 2*kd+1; i++ {
|
|
k := 1000 * i
|
|
tr.Set(uint64(k), getDummyC(1))
|
|
}
|
|
tr.Delete(1000 * kd)
|
|
for i := 0; i < kd; i++ {
|
|
tr.Set(uint64(1000*(kd+1)-1-i), getDummyC(1))
|
|
}
|
|
k := 1000*(kd+1) - 1 - kd
|
|
tr.Set(uint64(k), getDummyC(1))
|
|
if _, ok := tr.Get(uint64(k)); !ok {
|
|
t.Fatalf("key lost: %v", k)
|
|
}
|
|
}
|