+11
@@ -0,0 +1,11 @@
|
||||
This package is a fork of github.com/jba/btree at commit
|
||||
d4edd57f39b8425fc2c631047ff4dc6024d82a4f, which itself was a fork of
|
||||
github.com/google/btree at 316fb6d3f031ae8f4d457c6c5186b9e3ded70435.
|
||||
|
||||
This directory makes the following modifications:
|
||||
|
||||
- Updated copyright notice.
|
||||
- removed LICENSE (it is the same as the repo-wide license, Apache 2.0)
|
||||
- Removed examples_test.go and .travis.yml.
|
||||
- Added this file.
|
||||
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package btree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const benchmarkTreeSize = 10000
|
||||
|
||||
var degrees = []int{2, 8, 32, 64}
|
||||
|
||||
func BenchmarkInsert(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
i := 0
|
||||
for i < b.N {
|
||||
tr := New(d, less)
|
||||
for _, m := range insertP {
|
||||
tr.Set(m.Key, m.Value)
|
||||
i++
|
||||
if i >= b.N {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeleteInsert(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
tr := New(d, less)
|
||||
for _, m := range insertP {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m := insertP[i%benchmarkTreeSize]
|
||||
tr.Delete(m.Key)
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeleteInsertCloneOnce(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
tr := New(d, less)
|
||||
for _, m := range insertP {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
tr = tr.Clone()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m := insertP[i%benchmarkTreeSize]
|
||||
tr.Delete(m.Key)
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeleteInsertCloneEachTime(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
tr := New(d, less)
|
||||
for _, m := range insertP {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr = tr.Clone()
|
||||
m := insertP[i%benchmarkTreeSize]
|
||||
tr.Delete(m.Key)
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDelete(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
removeP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
i := 0
|
||||
for i < b.N {
|
||||
b.StopTimer()
|
||||
tr := New(d, less)
|
||||
for _, v := range insertP {
|
||||
tr.Set(v.Key, v.Value)
|
||||
}
|
||||
b.StartTimer()
|
||||
for _, m := range removeP {
|
||||
tr.Delete(m.Key)
|
||||
i++
|
||||
if i >= b.N {
|
||||
return
|
||||
}
|
||||
}
|
||||
if tr.Len() > 0 {
|
||||
panic(tr.Len())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGet(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
getP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
i := 0
|
||||
for i < b.N {
|
||||
b.StopTimer()
|
||||
tr := New(d, less)
|
||||
for _, v := range insertP {
|
||||
tr.Set(v.Key, v.Value)
|
||||
}
|
||||
b.StartTimer()
|
||||
for _, m := range getP {
|
||||
tr.Get(m.Key)
|
||||
i++
|
||||
if i >= b.N {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetWithIndex(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
getP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
i := 0
|
||||
for i < b.N {
|
||||
b.StopTimer()
|
||||
tr := New(d, less)
|
||||
for _, v := range insertP {
|
||||
tr.Set(v.Key, v.Value)
|
||||
}
|
||||
b.StartTimer()
|
||||
for _, m := range getP {
|
||||
tr.GetWithIndex(m.Key)
|
||||
i++
|
||||
if i >= b.N {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetCloneEachTime(b *testing.B) {
|
||||
insertP := perm(benchmarkTreeSize)
|
||||
getP := perm(benchmarkTreeSize)
|
||||
for _, d := range degrees {
|
||||
b.Run(fmt.Sprintf("degree=%d", d), func(b *testing.B) {
|
||||
i := 0
|
||||
for i < b.N {
|
||||
b.StopTimer()
|
||||
tr := New(d, less)
|
||||
for _, m := range insertP {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
b.StartTimer()
|
||||
for _, m := range getP {
|
||||
tr = tr.Clone()
|
||||
tr.Get(m.Key)
|
||||
i++
|
||||
if i >= b.N {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFind(b *testing.B) {
|
||||
for _, d := range degrees {
|
||||
var items []item
|
||||
for i := 0; i < 2*d; i++ {
|
||||
items = append(items, item{i, i})
|
||||
}
|
||||
b.Run(fmt.Sprintf("size=%d", len(items)), func(b *testing.B) {
|
||||
for _, alg := range []struct {
|
||||
name string
|
||||
fun func(Key, []item) (int, bool)
|
||||
}{
|
||||
{"binary", findBinary},
|
||||
{"linear", findLinear},
|
||||
} {
|
||||
b.Run(alg.name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < len(items); j++ {
|
||||
alg.fun(items[j].key, items)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func findBinary(k Key, s []item) (int, bool) {
|
||||
i := sort.Search(len(s), func(i int) bool { return less(k, s[i].key) })
|
||||
// i is the smallest index of s for which key.Less(s[i].Key), or len(s).
|
||||
if i > 0 && !less(s[i-1], k) {
|
||||
return i - 1, true
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
func findLinear(k Key, s []item) (int, bool) {
|
||||
var i int
|
||||
for i = 0; i < len(s); i++ {
|
||||
if less(k, s[i].key) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i > 0 && !less(s[i-1].key, k) {
|
||||
return i - 1, true
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
type byInts []item
|
||||
|
||||
func (a byInts) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a byInts) Less(i, j int) bool {
|
||||
return a[i].key.(int) < a[j].key.(int)
|
||||
}
|
||||
|
||||
func (a byInts) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
+1018
File diff suppressed because it is too large
Load Diff
+422
@@ -0,0 +1,422 @@
|
||||
// Copyright 2014 Google Inc.
|
||||
// Modified 2018 by Jonathan Amsterdam (jbamsterdam@gmail.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package btree
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
seed := time.Now().Unix()
|
||||
fmt.Println(seed)
|
||||
rand.Seed(seed)
|
||||
}
|
||||
|
||||
type itemWithIndex struct {
|
||||
Key Key
|
||||
Value Value
|
||||
Index int
|
||||
}
|
||||
|
||||
// perm returns a random permutation of n Int items in the range [0, n).
|
||||
func perm(n int) []itemWithIndex {
|
||||
var out []itemWithIndex
|
||||
for _, v := range rand.Perm(n) {
|
||||
out = append(out, itemWithIndex{v, v, v})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// rang returns an ordered list of Int items in the range [0, n).
|
||||
func rang(n int) []itemWithIndex {
|
||||
var out []itemWithIndex
|
||||
for i := 0; i < n; i++ {
|
||||
out = append(out, itemWithIndex{i, i, i})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// all extracts all items from an iterator.
|
||||
func all(it *Iterator) []itemWithIndex {
|
||||
var out []itemWithIndex
|
||||
for it.Next() {
|
||||
out = append(out, itemWithIndex{it.Key, it.Value, it.Index})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// rangerev returns a reversed ordered list of Int items in the range [0, n).
|
||||
func rangrev(n int) []itemWithIndex {
|
||||
var out []itemWithIndex
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
out = append(out, itemWithIndex{i, i, i})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func reverse(s []itemWithIndex) {
|
||||
for i := 0; i < len(s)/2; i++ {
|
||||
s[i], s[len(s)-i-1] = s[len(s)-i-1], s[i]
|
||||
}
|
||||
}
|
||||
|
||||
var btreeDegree = flag.Int("degree", 32, "B-Tree degree")
|
||||
|
||||
func TestBTree(t *testing.T) {
|
||||
tr := New(*btreeDegree, less)
|
||||
const treeSize = 10000
|
||||
for i := 0; i < 10; i++ {
|
||||
if min, _ := tr.Min(); min != nil {
|
||||
t.Fatalf("empty min, got %+v", min)
|
||||
}
|
||||
if max, _ := tr.Max(); max != nil {
|
||||
t.Fatalf("empty max, got %+v", max)
|
||||
}
|
||||
for _, m := range perm(treeSize) {
|
||||
if _, ok := tr.Set(m.Key, m.Value); ok {
|
||||
t.Fatal("set found item", m)
|
||||
}
|
||||
}
|
||||
for _, m := range perm(treeSize) {
|
||||
_, ok, idx := tr.SetWithIndex(m.Key, m.Value)
|
||||
if !ok {
|
||||
t.Fatal("set didn't find item", m)
|
||||
}
|
||||
if idx != m.Index {
|
||||
t.Fatalf("got index %d, want %d", idx, m.Index)
|
||||
}
|
||||
}
|
||||
mink, minv := tr.Min()
|
||||
if want := 0; mink != want || minv != want {
|
||||
t.Fatalf("min: want %+v, got %+v, %+v", want, mink, minv)
|
||||
}
|
||||
maxk, maxv := tr.Max()
|
||||
if want := treeSize - 1; maxk != want || maxv != want {
|
||||
t.Fatalf("max: want %+v, got %+v, %+v", want, maxk, maxv)
|
||||
}
|
||||
got := all(tr.BeforeIndex(0))
|
||||
want := rang(treeSize)
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
|
||||
}
|
||||
|
||||
for _, m := range perm(treeSize) {
|
||||
if _, removed := tr.Delete(m.Key); !removed {
|
||||
t.Fatalf("didn't find %v", m)
|
||||
}
|
||||
}
|
||||
if got = all(tr.BeforeIndex(0)); len(got) > 0 {
|
||||
t.Fatalf("some left!: %v", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAt(t *testing.T) {
|
||||
tr := New(*btreeDegree, less)
|
||||
for _, m := range perm(100) {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
for i := 0; i < tr.Len(); i++ {
|
||||
gotk, gotv := tr.At(i)
|
||||
if want := i; gotk != want || gotv != want {
|
||||
t.Fatalf("At(%d) = (%v, %v), want (%v, %v)", i, gotk, gotv, want, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWithIndex(t *testing.T) {
|
||||
tr := New(*btreeDegree, less)
|
||||
for _, m := range perm(100) {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
for i := 0; i < tr.Len(); i++ {
|
||||
gotv, goti := tr.GetWithIndex(i)
|
||||
wantv, wanti := i, i
|
||||
if gotv != wantv || goti != wanti {
|
||||
t.Errorf("GetWithIndex(%d) = (%v, %v), want (%v, %v)",
|
||||
i, gotv, goti, wantv, wanti)
|
||||
}
|
||||
}
|
||||
_, got := tr.GetWithIndex(100)
|
||||
if want := -1; got != want {
|
||||
t.Errorf("got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetWithIndex(t *testing.T) {
|
||||
tr := New(4, less) // use a small degree to cover more cases
|
||||
var contents []int
|
||||
for _, m := range perm(100) {
|
||||
_, _, idx := tr.SetWithIndex(m.Key, m.Value)
|
||||
contents = append(contents, m.Index)
|
||||
sort.Ints(contents)
|
||||
want := -1
|
||||
for i, c := range contents {
|
||||
if c == m.Index {
|
||||
want = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx != want {
|
||||
t.Fatalf("got %d, want %d", idx, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMin(t *testing.T) {
|
||||
tr := New(3, less)
|
||||
for _, m := range perm(100) {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
var got []itemWithIndex
|
||||
for i := 0; tr.Len() > 0; i++ {
|
||||
k, v := tr.DeleteMin()
|
||||
got = append(got, itemWithIndex{k, v, i})
|
||||
}
|
||||
if want := rang(100); !cmp.Equal(got, want) {
|
||||
t.Fatalf("got: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMax(t *testing.T) {
|
||||
tr := New(3, less)
|
||||
for _, m := range perm(100) {
|
||||
tr.Set(m.Key, m.Value)
|
||||
}
|
||||
var got []itemWithIndex
|
||||
for tr.Len() > 0 {
|
||||
k, v := tr.DeleteMax()
|
||||
got = append(got, itemWithIndex{k, v, tr.Len()})
|
||||
}
|
||||
reverse(got)
|
||||
if want := rang(100); !cmp.Equal(got, want) {
|
||||
t.Fatalf("got: %v\nwant: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
const size = 10
|
||||
|
||||
tr := New(2, less)
|
||||
// Empty tree.
|
||||
for i, it := range []*Iterator{
|
||||
tr.BeforeIndex(0),
|
||||
tr.Before(3),
|
||||
tr.After(3),
|
||||
} {
|
||||
if got, want := it.Next(), false; got != want {
|
||||
t.Errorf("empty, #%d: got %t, want %t", i, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Root with zero children.
|
||||
tr.Set(1, nil)
|
||||
tr.Delete(1)
|
||||
if !(tr.root != nil && len(tr.root.children) == 0 && len(tr.root.items) == 0) {
|
||||
t.Fatal("wrong shape tree")
|
||||
}
|
||||
for i, it := range []*Iterator{
|
||||
tr.BeforeIndex(0),
|
||||
tr.Before(3),
|
||||
tr.After(3),
|
||||
} {
|
||||
if got, want := it.Next(), false; got != want {
|
||||
t.Errorf("zero root, #%d: got %t, want %t", i, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Tree with size elements.
|
||||
p := perm(size)
|
||||
for _, v := range p {
|
||||
tr.Set(v.Key, v.Value)
|
||||
}
|
||||
|
||||
it := tr.BeforeIndex(0)
|
||||
got := all(it)
|
||||
want := rang(size)
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Fatalf("got %+v\nwant %+v\n", got, want)
|
||||
}
|
||||
|
||||
for i, w := range want {
|
||||
it := tr.Before(w.Key)
|
||||
got = all(it)
|
||||
wn := want[w.Key.(int):]
|
||||
if !cmp.Equal(got, wn) {
|
||||
t.Fatalf("got %+v\nwant %+v\n", got, wn)
|
||||
}
|
||||
|
||||
it = tr.BeforeIndex(i)
|
||||
got = all(it)
|
||||
if !cmp.Equal(got, wn) {
|
||||
t.Fatalf("got %+v\nwant %+v\n", got, wn)
|
||||
}
|
||||
|
||||
it = tr.After(w.Key)
|
||||
got = all(it)
|
||||
wn = append([]itemWithIndex(nil), want[:w.Key.(int)+1]...)
|
||||
reverse(wn)
|
||||
if !cmp.Equal(got, wn) {
|
||||
t.Fatalf("got %+v\nwant %+v\n", got, wn)
|
||||
}
|
||||
|
||||
it = tr.AfterIndex(i)
|
||||
got = all(it)
|
||||
if !cmp.Equal(got, wn) {
|
||||
t.Fatalf("got %+v\nwant %+v\n", got, wn)
|
||||
}
|
||||
}
|
||||
|
||||
// Non-existent keys.
|
||||
tr = New(2, less)
|
||||
for _, v := range p {
|
||||
tr.Set(v.Key.(int)*2, v.Value)
|
||||
}
|
||||
// tr has only even keys: 0, 2, 4, ... Iterate from odd keys.
|
||||
for i := -1; i <= size+1; i += 2 {
|
||||
it := tr.Before(i)
|
||||
got := all(it)
|
||||
var want []itemWithIndex
|
||||
for j := (i + 1) / 2; j < size; j++ {
|
||||
want = append(want, itemWithIndex{j * 2, j, j})
|
||||
}
|
||||
if !cmp.Equal(got, want) {
|
||||
tr.print(os.Stdout)
|
||||
t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want)
|
||||
}
|
||||
|
||||
it = tr.After(i)
|
||||
got = all(it)
|
||||
want = nil
|
||||
for j := (i - 1) / 2; j >= 0; j-- {
|
||||
want = append(want, itemWithIndex{j * 2, j, j})
|
||||
}
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMixed(t *testing.T) {
|
||||
// Test random, mixed insertions and deletions.
|
||||
const maxSize = 1000
|
||||
tr := New(3, less)
|
||||
has := map[int]bool{}
|
||||
for i := 0; i < 10000; i++ {
|
||||
r := rand.Intn(maxSize)
|
||||
if r >= tr.Len() {
|
||||
old, ok := tr.Set(r, r)
|
||||
if has[r] != ok {
|
||||
t.Fatalf("%d: has=%t, ok=%t", r, has[r], ok)
|
||||
}
|
||||
if ok && old.(int) != r {
|
||||
t.Fatalf("%d: bad old", r)
|
||||
}
|
||||
has[r] = true
|
||||
if got, want := tr.Get(r), r; got != want {
|
||||
t.Fatalf("Get(%d) = %d, want %d", r, got, want)
|
||||
}
|
||||
} else {
|
||||
// Expoit random map iteration order.
|
||||
var d int
|
||||
for d = range has {
|
||||
break
|
||||
}
|
||||
old, removed := tr.Delete(d)
|
||||
if !removed {
|
||||
t.Fatalf("%d not found", d)
|
||||
}
|
||||
if old.(int) != d {
|
||||
t.Fatalf("%d: bad old", d)
|
||||
}
|
||||
delete(has, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cloneTestSize = 10000
|
||||
|
||||
func cloneTest(t *testing.T, b *BTree, start int, p []itemWithIndex, wg *sync.WaitGroup, treec chan<- *BTree) {
|
||||
treec <- b
|
||||
for i := start; i < cloneTestSize; i++ {
|
||||
b.Set(p[i].Key, p[i].Value)
|
||||
if i%(cloneTestSize/5) == 0 {
|
||||
wg.Add(1)
|
||||
go cloneTest(t, b.Clone(), i+1, p, wg, treec)
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func TestCloneConcurrentOperations(t *testing.T) {
|
||||
b := New(*btreeDegree, less)
|
||||
treec := make(chan *BTree)
|
||||
p := perm(cloneTestSize)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go cloneTest(t, b, 0, p, &wg, treec)
|
||||
var trees []*BTree
|
||||
donec := make(chan struct{})
|
||||
go func() {
|
||||
for t := range treec {
|
||||
trees = append(trees, t)
|
||||
}
|
||||
close(donec)
|
||||
}()
|
||||
wg.Wait()
|
||||
close(treec)
|
||||
<-donec
|
||||
want := rang(cloneTestSize)
|
||||
for i, tree := range trees {
|
||||
if !cmp.Equal(want, all(tree.BeforeIndex(0))) {
|
||||
t.Errorf("tree %v mismatch", i)
|
||||
}
|
||||
}
|
||||
toRemove := rang(cloneTestSize)[cloneTestSize/2:]
|
||||
for i := 0; i < len(trees)/2; i++ {
|
||||
tree := trees[i]
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for _, m := range toRemove {
|
||||
tree.Delete(m.Key)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
for i, tree := range trees {
|
||||
var wantpart []itemWithIndex
|
||||
if i < len(trees)/2 {
|
||||
wantpart = want[:cloneTestSize/2]
|
||||
} else {
|
||||
wantpart = want
|
||||
}
|
||||
if got := all(tree.BeforeIndex(0)); !cmp.Equal(wantpart, got) {
|
||||
t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func less(a, b interface{}) bool { return a.(int) < b.(int) }
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package btree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (t *BTree) print(w io.Writer) {
|
||||
t.root.print(w, 0)
|
||||
}
|
||||
|
||||
func (n *node) print(w io.Writer, level int) {
|
||||
indent := strings.Repeat(" ", level)
|
||||
if n == nil {
|
||||
fmt.Fprintf(w, "%s<nil>\n", indent)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "%s%v\n", indent, n.items)
|
||||
for _, c := range n.children {
|
||||
c.print(w, level+1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user