mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
1408 lines
29 KiB
Go
1408 lines
29 KiB
Go
package bptree
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"runtime/debug"
|
|
"sort"
|
|
"sync"
|
|
"testing"
|
|
|
|
crand "crypto/rand"
|
|
"encoding/binary"
|
|
mrand "math/rand"
|
|
|
|
)
|
|
|
|
var rand *mrand.Rand
|
|
|
|
func init() {
|
|
seed := make([]byte, 8)
|
|
if _, err := crand.Read(seed); err == nil {
|
|
rand = ThreadSafeRand(int64(binary.BigEndian.Uint64(seed)))
|
|
} else {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func randslice(length int) []byte {
|
|
return RandSlice(length)
|
|
}
|
|
|
|
func randstr(length int) String {
|
|
return String(RandStr(length))
|
|
}
|
|
|
|
type Strings []String
|
|
|
|
func (self Strings) Len() int {
|
|
return len(self)
|
|
}
|
|
|
|
func (self Strings) Less(i, j int) bool {
|
|
return self[i].Less(self[j])
|
|
}
|
|
|
|
func (self Strings) Swap(i, j int) {
|
|
self[i], self[j] = self[j], self[i]
|
|
}
|
|
|
|
type record struct {
|
|
key String
|
|
value ItemValue
|
|
}
|
|
|
|
type records []*record
|
|
|
|
func (self records) Len() int {
|
|
return len(self)
|
|
}
|
|
|
|
func (self records) Less(i, j int) bool {
|
|
return self[i].key.Less(self[j].key)
|
|
}
|
|
|
|
func (self records) Swap(i, j int) {
|
|
self[i], self[j] = self[j], self[i]
|
|
}
|
|
|
|
func BenchmarkBpTree(b *testing.B) {
|
|
b.StopTimer()
|
|
|
|
recs := make(records, 100)
|
|
ranrec := func() *record {
|
|
return &record{randstr(20), randstr(20)}
|
|
}
|
|
|
|
for i := range recs {
|
|
recs[i] = ranrec()
|
|
}
|
|
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
t := NewBpTree(23)
|
|
for _, r := range recs {
|
|
t.Add(r.key, r.value)
|
|
}
|
|
for _, r := range recs {
|
|
t.RemoveWhere(r.key, func(value ItemValue) bool { return true })
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddHasCountFindIterateRemove(t *testing.T) {
|
|
|
|
ranrec := func() *record {
|
|
return &record{
|
|
randstr(12),
|
|
randstr(12),
|
|
}
|
|
}
|
|
|
|
test := func(bpt *BpTree) {
|
|
var err error
|
|
recs := make(records, 128)
|
|
new_recs := make(records, 128)
|
|
for i := range recs {
|
|
r := ranrec()
|
|
recs[i] = r
|
|
new_recs[i] = &record{r.key, randstr(12)}
|
|
err = bpt.Add(r.key, r.value)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
for i, r := range recs {
|
|
if has := bpt.Has(r.key); !has {
|
|
t.Error(bpt, "Missing key")
|
|
}
|
|
if has := bpt.Has(randstr(10)); has {
|
|
t.Error("Table has extra key")
|
|
}
|
|
if count := bpt.Count(r.key); count != 1 {
|
|
t.Error(bpt, "Missing key")
|
|
}
|
|
if count := bpt.Count(randstr(10)); count != 0 {
|
|
t.Error("Table has extra key")
|
|
}
|
|
for k, v, next := bpt.Find(r.key)(); next != nil; k, v, next = next() {
|
|
if !k.Equals(r.key) {
|
|
t.Error(bpt, "Find Failed Key Error")
|
|
}
|
|
if !v.(String).Equals(r.value) {
|
|
t.Error(bpt, "Find Failed Value Error")
|
|
}
|
|
}
|
|
err = bpt.Replace(r.key, func(value ItemValue) bool { return true }, new_recs[i].value)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
sort.Sort(recs)
|
|
sort.Sort(new_recs)
|
|
i := 0
|
|
for k, v, next := bpt.Iterate()(); next != nil; k, v, next = next() {
|
|
if !recs[i].key.Equals(k) {
|
|
t.Error("iterate error wrong key")
|
|
}
|
|
if !new_recs[i].value.Equals(v.(String)) {
|
|
t.Error("iterate error wrong value")
|
|
}
|
|
i++
|
|
}
|
|
i = len(recs) - 1
|
|
for k, v, next := bpt.Backward()(); next != nil; k, v, next = next() {
|
|
if !recs[i].key.Equals(k) {
|
|
t.Error("iterate error wrong key")
|
|
}
|
|
if !new_recs[i].value.Equals(v.(String)) {
|
|
t.Error("iterate error wrong value")
|
|
}
|
|
i--
|
|
}
|
|
i = 0
|
|
for k, next := bpt.Keys()(); next != nil; k, next = next() {
|
|
if !recs[i].key.Equals(k) {
|
|
t.Error("iterate error wrong key")
|
|
}
|
|
i++
|
|
}
|
|
i = 7
|
|
for k, v, next := bpt.Range(recs[i].key, recs[i+(len(recs)/2)].key)(); next != nil; k, v, next = next() {
|
|
if !recs[i].key.Equals(k) {
|
|
t.Error("iterate error wrong key")
|
|
}
|
|
if !new_recs[i].value.Equals(v.(String)) {
|
|
t.Error("iterate error wrong value")
|
|
}
|
|
i++
|
|
}
|
|
for k, v, next := bpt.Range(recs[i].key, recs[7].key)(); next != nil; k, v, next = next() {
|
|
if !recs[i].key.Equals(k) {
|
|
t.Error("iterate error wrong key")
|
|
}
|
|
if !new_recs[i].value.Equals(v.(String)) {
|
|
t.Error("iterate error wrong value", k, v, recs[i].value, new_recs[i].value)
|
|
}
|
|
i--
|
|
}
|
|
for i, r := range recs {
|
|
if has := bpt.Has(r.key); !has {
|
|
t.Error(bpt, "Missing key")
|
|
}
|
|
if count := bpt.Count(r.key); count != 1 {
|
|
t.Error(bpt, "Missing key")
|
|
}
|
|
if err := bpt.RemoveWhere(r.key, func(value ItemValue) bool { return true }); err != nil {
|
|
t.Fatal(bpt, err)
|
|
}
|
|
if has := bpt.Has(r.key); has {
|
|
t.Error("Table has extra key")
|
|
}
|
|
for _, x := range recs[i+1:] {
|
|
if has := bpt.Has(x.key); !has {
|
|
t.Error(bpt, "Missing key", x.key)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for i := 2; i < 64; i++ {
|
|
test(NewBpTree(i))
|
|
}
|
|
}
|
|
|
|
func TestBpMap(t *testing.T) {
|
|
|
|
ranrec := func() *record {
|
|
return &record{
|
|
randstr(12),
|
|
randstr(12),
|
|
}
|
|
}
|
|
|
|
test := func(table MapOperable) {
|
|
recs := make(records, 400)
|
|
for i := range recs {
|
|
r := ranrec()
|
|
recs[i] = r
|
|
err := table.Put(r.key, String(""))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = table.Put(r.key, r.value)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
for _, r := range recs {
|
|
if has := table.Has(r.key); !has {
|
|
t.Error(table, "Missing key")
|
|
}
|
|
if has := table.Has(randstr(12)); has {
|
|
t.Error("Table has extra key")
|
|
}
|
|
if val, err := table.Get(r.key); err != nil {
|
|
t.Error(err)
|
|
} else if !(val.(String)).Equals(r.value) {
|
|
t.Error("wrong value")
|
|
}
|
|
}
|
|
|
|
for i, x := range recs {
|
|
if val, err := table.Remove(x.key); err != nil {
|
|
t.Error(err)
|
|
} else if !(val.(String)).Equals(x.value) {
|
|
t.Error("wrong value")
|
|
}
|
|
for _, r := range recs[i+1:] {
|
|
if has := table.Has(r.key); !has {
|
|
t.Error("Missing key")
|
|
}
|
|
if has := table.Has(randstr(12)); has {
|
|
t.Error("Table has extra key")
|
|
}
|
|
if val, err := table.Get(r.key); err != nil {
|
|
t.Error(err)
|
|
} else if !(val.(String)).Equals(r.value) {
|
|
t.Error("wrong value")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
test(NewBpMap(23))
|
|
}
|
|
|
|
func Test_get_start(t *testing.T) {
|
|
root := NewLeaf(2)
|
|
root, err := root.put(Int(1), Int(1))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
root, err = root.put(Int(5), Int(3))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
root, err = root.put(Int(3), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
t.Log(root)
|
|
t.Log(root.pointers[0])
|
|
t.Log(root.pointers[1])
|
|
i, n := root.get_start(Int(1))
|
|
if n != root.pointers[0] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 0 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
i, n = root.get_start(Int(3))
|
|
if n != root.pointers[0] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 1 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
i, n = root.get_start(Int(5))
|
|
if n != root.pointers[1] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 0 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
i, n = root.get_start(Int(2))
|
|
if n != root.pointers[0] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 1 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
i, n = root.get_start(Int(4))
|
|
t.Log(n)
|
|
if n != root.pointers[1] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 0 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
i, n = root.get_start(Int(0))
|
|
if n != root.pointers[0] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 0 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
i, n = root.get_start(Int(5))
|
|
if n != root.pointers[1] {
|
|
t.Error("wrong node from get_start")
|
|
}
|
|
if i != 0 {
|
|
t.Error("wrong index from get_start")
|
|
}
|
|
}
|
|
|
|
func Test_get_end(t *testing.T) {
|
|
root := NewLeaf(3)
|
|
root, err := root.put(Int(1), Int(1))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(4), Int(4))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(3), Int(3))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(8), Int(8))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(9), Int(9))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(10), Int(10))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(6), Int(6))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(7), Int(7))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root, err = root.put(Int(5), Int(5))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Log(root)
|
|
t.Log(root.pointers[0])
|
|
t.Log(root.pointers[1])
|
|
printTree(root, "")
|
|
}
|
|
|
|
func Test_put_no_root_split(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, err := a.put(Int(1), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if !p.has(Int(1)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
}
|
|
p, err = a.put(Int(1), Int(3))
|
|
|
|
t.Log(a)
|
|
printTree(a, "")
|
|
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if !p.has(Int(1)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
t.Log(p)
|
|
t.Log(p.getNext())
|
|
}
|
|
}
|
|
|
|
func Test_put_root_split(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
p, err := a.put(Int(1), Int(1))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if !p.has(Int(1)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
}
|
|
p, err = a.put(Int(3), Int(3))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(3)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
}
|
|
p, err = a.put(Int(2), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p == a {
|
|
t.Errorf("p == a")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(3)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if len(p.pointers) != 2 {
|
|
t.Error("p didn't have right number of pointers", p)
|
|
}
|
|
if !p.pointers[0].has(Int(1)) || !p.pointers[0].has(Int(2)) {
|
|
t.Error("p.pointers[0] didn't have the right keys", p.pointers[0])
|
|
}
|
|
if !p.pointers[1].has(Int(3)) {
|
|
t.Error("p.pointers[1] didn't have the right keys", p.pointers[1])
|
|
}
|
|
t.Log(p)
|
|
t.Log(p.pointers[0])
|
|
t.Log(p.pointers[1])
|
|
}
|
|
}
|
|
|
|
func Test_internal_insert_no_split(t *testing.T) {
|
|
a := NewInternal(3)
|
|
leaf := NewLeaf(1)
|
|
if err := leaf.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(1), leaf); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(5), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.internal_insert(Int(2), nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q != nil {
|
|
t.Errorf("q != nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(2)) || !p.has(Int(5)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_internal_insert_split_less(t *testing.T) {
|
|
a := NewInternal(3)
|
|
leaf := NewLeaf(1)
|
|
if err := leaf.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(1), leaf); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(3), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(5), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.internal_insert(Int(2), nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(2)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(3)) || !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_internal_split_less(t *testing.T) {
|
|
a := NewInternal(3)
|
|
if err := a.put_kp(Int(1), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(3), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(5), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.internal_split(Int(2), nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(2)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(3)) || !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_internal_split_equal(t *testing.T) {
|
|
a := NewInternal(3)
|
|
if err := a.put_kp(Int(1), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(3), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(5), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.internal_split(Int(3), nil)
|
|
if err == nil {
|
|
t.Error("split succeeded should have failed", p, q)
|
|
}
|
|
}
|
|
|
|
func Test_internal_split_greater(t *testing.T) {
|
|
a := NewInternal(3)
|
|
if err := a.put_kp(Int(1), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(3), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(5), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.internal_split(Int(4), nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(3)) || !p.has(Int(4)){
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_leaf_insert_no_split(t *testing.T) {
|
|
a := NewLeaf(3)
|
|
insert_linked_list_node(a, nil, nil)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.leaf_insert(Int(2), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q != nil {
|
|
t.Errorf("q != nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(2)) || !p.has(Int(3)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
}
|
|
}
|
|
|
|
// tests the defer to split logic
|
|
func Test_leaf_insert_split_less(t *testing.T) {
|
|
a := NewLeaf(3)
|
|
insert_linked_list_node(a, nil, nil)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(5), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.leaf_insert(Int(2), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(2)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(3)) || !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_leaf_split_less(t *testing.T) {
|
|
a := NewLeaf(3)
|
|
insert_linked_list_node(a, nil, nil)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(5), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.leaf_split(Int(2), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(2)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(3)) || !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_leaf_split_equal(t *testing.T) {
|
|
a := NewLeaf(3)
|
|
insert_linked_list_node(a, nil, nil)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(5), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.leaf_split(Int(3), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(3)) || !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_leaf_split_greater(t *testing.T) {
|
|
a := NewLeaf(3)
|
|
insert_linked_list_node(a, nil, nil)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(5), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.leaf_split(Int(4), Int(2))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil {
|
|
t.Errorf("q == nil")
|
|
}
|
|
if !p.has(Int(1)) || !p.has(Int(3)) || !p.has(Int(4)) {
|
|
t.Error("p didn't have the right keys", p)
|
|
}
|
|
if !q.has(Int(5)) {
|
|
t.Error("q didn't have the right keys", q)
|
|
}
|
|
}
|
|
}
|
|
|
|
// tests the defer logic
|
|
func Test_pure_leaf_insert_split_less(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(2)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(2)
|
|
insert_linked_list_node(d, c, nil)
|
|
if err := a.put_kv(Int(3), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(4)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := d.put_kv(Int(4), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.leaf_insert(Int(2), Int(1))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if q != a {
|
|
t.Errorf("q != a")
|
|
}
|
|
if p == nil || len(p.keys) != 1 || !p.keys[0].Equals(Int(2)) {
|
|
t.Errorf("p did not contain the right key")
|
|
}
|
|
if p.getPrev() != nil {
|
|
t.Errorf("expected p.prev == nil")
|
|
}
|
|
if p.getNext() != a {
|
|
t.Errorf("expected p.next == a")
|
|
}
|
|
if a.getPrev() != p {
|
|
t.Errorf("expected a.prev == p")
|
|
}
|
|
if a.getNext() != b {
|
|
t.Errorf("expected a.next == b")
|
|
}
|
|
if b.getPrev() != a {
|
|
t.Errorf("expected b.prev == a")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != d {
|
|
t.Errorf("expected c.next == d")
|
|
}
|
|
if d.getPrev() != c {
|
|
t.Errorf("expected d.prev == c")
|
|
}
|
|
if d.getNext() != nil {
|
|
t.Errorf("expected d.next == nil")
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_pure_leaf_split_less(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(2)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(2)
|
|
insert_linked_list_node(d, c, nil)
|
|
if err := a.put_kv(Int(3), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(4)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := d.put_kv(Int(4), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.pure_leaf_split(Int(2), Int(1))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if q != a {
|
|
t.Errorf("q != a")
|
|
}
|
|
if p == nil || len(p.keys) != 1 || !p.keys[0].Equals(Int(2)) {
|
|
t.Errorf("p did not contain the right key")
|
|
}
|
|
if p.getPrev() != nil {
|
|
t.Errorf("expected p.prev == nil")
|
|
}
|
|
if p.getNext() != a {
|
|
t.Errorf("expected p.next == a")
|
|
}
|
|
if a.getPrev() != p {
|
|
t.Errorf("expected a.prev == p")
|
|
}
|
|
if a.getNext() != b {
|
|
t.Errorf("expected a.next == b")
|
|
}
|
|
if b.getPrev() != a {
|
|
t.Errorf("expected b.prev == a")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != d {
|
|
t.Errorf("expected c.next == d")
|
|
}
|
|
if d.getPrev() != c {
|
|
t.Errorf("expected d.prev == c")
|
|
}
|
|
if d.getNext() != nil {
|
|
t.Errorf("expected d.next == nil")
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_pure_leaf_split_equal(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(2)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(2)
|
|
insert_linked_list_node(d, c, nil)
|
|
if err := a.put_kv(Int(3), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(4)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := d.put_kv(Int(4), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.pure_leaf_split(Int(3), Int(1))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q != nil {
|
|
t.Errorf("q != nil")
|
|
}
|
|
if a.getPrev() != nil {
|
|
t.Errorf("expected a.prev == nil")
|
|
}
|
|
if a.getNext() != b {
|
|
t.Errorf("expected a.next == b")
|
|
}
|
|
if b.getPrev() != a {
|
|
t.Errorf("expected b.prev == a")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != d {
|
|
t.Errorf("expected c.next == d")
|
|
}
|
|
if d.getPrev() != c {
|
|
t.Errorf("expected d.prev == c")
|
|
}
|
|
if d.getNext() != nil {
|
|
t.Errorf("expected d.next == nil")
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_pure_leaf_split_greater(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(2)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(2)
|
|
insert_linked_list_node(d, c, nil)
|
|
if err := a.put_kv(Int(3), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(4)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := d.put_kv(Int(5), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
p, q, err := a.pure_leaf_split(Int(4), Int(1))
|
|
if err != nil {
|
|
t.Error(err)
|
|
} else {
|
|
if p != a {
|
|
t.Errorf("p != a")
|
|
}
|
|
if q == nil || len(q.keys) != 1 || !q.keys[0].Equals(Int(4)) {
|
|
t.Errorf("q != nil")
|
|
}
|
|
if a.getPrev() != nil {
|
|
t.Errorf("expected a.prev == nil")
|
|
}
|
|
if a.getNext() != b {
|
|
t.Errorf("expected a.next == b")
|
|
}
|
|
if b.getPrev() != a {
|
|
t.Errorf("expected b.prev == a")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != q {
|
|
t.Errorf("expected c.next == q")
|
|
}
|
|
if q.getPrev() != c {
|
|
t.Errorf("expected q.prev == c")
|
|
}
|
|
if q.getNext() != d {
|
|
t.Errorf("expected q.next == d")
|
|
}
|
|
if d.getPrev() != q {
|
|
t.Errorf("expected d.prev == q")
|
|
}
|
|
if d.getNext() != nil {
|
|
t.Errorf("expected d.next == nil")
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_find_end_of_pure_run(t *testing.T) {
|
|
a := NewLeaf(2)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(2)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(2)
|
|
insert_linked_list_node(d, c, nil)
|
|
if err := a.put_kv(Int(3), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := b.put_kv(Int(3), Int(4)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := c.put_kv(Int(3), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := d.put_kv(Int(4), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
e := a.find_end_of_pure_run()
|
|
if e != c {
|
|
t.Errorf("end of run should have been block c %v %v", e, c)
|
|
}
|
|
}
|
|
|
|
func Test_insert_linked_list_node(t *testing.T) {
|
|
a := NewLeaf(1)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(3)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(4)
|
|
insert_linked_list_node(d, a, b)
|
|
if a.getPrev() != nil {
|
|
t.Errorf("expected a.prev == nil")
|
|
}
|
|
if a.getNext() != d {
|
|
t.Errorf("expected a.next == d")
|
|
}
|
|
if d.getPrev() != a {
|
|
t.Errorf("expected d.prev == a")
|
|
}
|
|
if d.getNext() != b {
|
|
t.Errorf("expected d.next == b")
|
|
}
|
|
if b.getPrev() != d {
|
|
t.Errorf("expected b.prev == d")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != nil {
|
|
t.Errorf("expected c.next == nil")
|
|
}
|
|
}
|
|
|
|
func Test_remove_linked_list_node(t *testing.T) {
|
|
a := NewLeaf(1)
|
|
insert_linked_list_node(a, nil, nil)
|
|
b := NewLeaf(2)
|
|
insert_linked_list_node(b, a, nil)
|
|
c := NewLeaf(3)
|
|
insert_linked_list_node(c, b, nil)
|
|
d := NewLeaf(4)
|
|
insert_linked_list_node(d, a, b)
|
|
if a.getPrev() != nil {
|
|
t.Errorf("expected a.prev == nil")
|
|
}
|
|
if a.getNext() != d {
|
|
t.Errorf("expected a.next == d")
|
|
}
|
|
if d.getPrev() != a {
|
|
t.Errorf("expected d.prev == a")
|
|
}
|
|
if d.getNext() != b {
|
|
t.Errorf("expected d.next == b")
|
|
}
|
|
if b.getPrev() != d {
|
|
t.Errorf("expected b.prev == d")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != nil {
|
|
t.Errorf("expected c.next == nil")
|
|
}
|
|
remove_linked_list_node(d)
|
|
if a.getPrev() != nil {
|
|
t.Errorf("expected a.prev == nil")
|
|
}
|
|
if a.getNext() != b {
|
|
t.Errorf("expected a.next == b")
|
|
}
|
|
if b.getPrev() != a {
|
|
t.Errorf("expected b.prev == a")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != nil {
|
|
t.Errorf("expected c.next == nil")
|
|
}
|
|
remove_linked_list_node(a)
|
|
if b.getPrev() != nil {
|
|
t.Errorf("expected b.prev == nil")
|
|
}
|
|
if b.getNext() != c {
|
|
t.Errorf("expected b.next == c")
|
|
}
|
|
if c.getPrev() != b {
|
|
t.Errorf("expected c.prev == b")
|
|
}
|
|
if c.getNext() != nil {
|
|
t.Errorf("expected c.next == nil")
|
|
}
|
|
remove_linked_list_node(c)
|
|
if b.getPrev() != nil {
|
|
t.Errorf("expected b.prev == nil")
|
|
}
|
|
if b.getNext() != nil {
|
|
t.Errorf("expected b.next == nil")
|
|
}
|
|
remove_linked_list_node(b)
|
|
}
|
|
|
|
func Test_balance_leaf_nodes_with_dup(t *testing.T) {
|
|
a := NewLeaf(3)
|
|
b := NewLeaf(3)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(2), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
balance_nodes(a, b, Int(2))
|
|
if !a.has(Int(1)) || a.has(Int(2)) {
|
|
t.Error("a had wrong items", a)
|
|
}
|
|
if !b.has(Int(2)) || b.has(Int(1)) {
|
|
t.Error("a had wrong items", b)
|
|
}
|
|
}
|
|
|
|
func Test_balance_leaf_nodes(t *testing.T) {
|
|
a := NewLeaf(7)
|
|
b := NewLeaf(7)
|
|
if err := a.put_kv(Int(1), Int(1)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(2), Int(2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(3), Int(3)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(4), Int(4)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(5), Int(5)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(6), Int(6)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kv(Int(7), Int(7)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
balance_nodes(a, b, Int(5))
|
|
for i, k := range a.keys {
|
|
if int(k.(Int)) != i+1 {
|
|
t.Errorf("k != %d", i+1)
|
|
}
|
|
}
|
|
for i, k := range b.keys {
|
|
if int(k.(Int)) != 5+i {
|
|
t.Errorf("k != %d", 5+i)
|
|
}
|
|
}
|
|
for i, v := range a.values {
|
|
if int(v.(Int)) != i+1 {
|
|
t.Errorf("k != %d", i+1)
|
|
}
|
|
}
|
|
for i, v := range b.values {
|
|
if int(v.(Int)) != 5+i {
|
|
t.Errorf("v != %d", 5+i)
|
|
}
|
|
}
|
|
t.Log(a)
|
|
t.Log(b)
|
|
}
|
|
|
|
func Test_balance_internal_nodes(t *testing.T) {
|
|
a := NewInternal(6)
|
|
b := NewInternal(6)
|
|
if err := a.put_kp(Int(1), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(2), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(3), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(4), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(5), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := a.put_kp(Int(6), nil); err != nil {
|
|
t.Error(err)
|
|
}
|
|
balance_nodes(a, b, Int(4))
|
|
for i, k := range a.keys {
|
|
if int(k.(Int)) != i+1 {
|
|
t.Errorf("k != %d", i+1)
|
|
}
|
|
}
|
|
for i, k := range b.keys {
|
|
if int(k.(Int)) != 3+i+1 {
|
|
t.Errorf("k != %d", 3+i+1)
|
|
}
|
|
}
|
|
t.Log(a)
|
|
t.Log(b)
|
|
}
|
|
|
|
|
|
// copied from
|
|
|
|
// ThreadSafeRand provides a thread safe version of math/rand.Rand using
|
|
// the same technique used in the math/rand package to make the top level
|
|
// functions thread safe.
|
|
func ThreadSafeRand(seed int64) *mrand.Rand {
|
|
return mrand.New(&lockedSource{src: mrand.NewSource(seed).(mrand.Source64)})
|
|
}
|
|
|
|
// from: https://golang.org/src/math/rand/rand.go?s=8161:8175#L317
|
|
type lockedSource struct {
|
|
lk sync.Mutex
|
|
src mrand.Source64
|
|
}
|
|
|
|
func (r *lockedSource) Int63() (n int64) {
|
|
r.lk.Lock()
|
|
n = r.src.Int63()
|
|
r.lk.Unlock()
|
|
return
|
|
}
|
|
|
|
func (r *lockedSource) Uint64() (n uint64) {
|
|
r.lk.Lock()
|
|
n = r.src.Uint64()
|
|
r.lk.Unlock()
|
|
return
|
|
}
|
|
|
|
func (r *lockedSource) Seed(seed int64) {
|
|
r.lk.Lock()
|
|
r.src.Seed(seed)
|
|
r.lk.Unlock()
|
|
}
|
|
|
|
// seedPos implements Seed for a lockedSource without a race condiiton.
|
|
func (r *lockedSource) seedPos(seed int64, readPos *int8) {
|
|
r.lk.Lock()
|
|
r.src.Seed(seed)
|
|
*readPos = 0
|
|
r.lk.Unlock()
|
|
}
|
|
|
|
// read implements Read for a lockedSource without a race condition.
|
|
func (r *lockedSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) {
|
|
r.lk.Lock()
|
|
n, err = read(p, r.src.Int63, readVal, readPos)
|
|
r.lk.Unlock()
|
|
return
|
|
}
|
|
|
|
func read(p []byte, int63 func() int64, readVal *int64, readPos *int8) (n int, err error) {
|
|
pos := *readPos
|
|
val := *readVal
|
|
for n = 0; n < len(p); n++ {
|
|
if pos == 0 {
|
|
val = int63()
|
|
pos = 7
|
|
}
|
|
p[n] = byte(val)
|
|
val >>= 8
|
|
pos--
|
|
}
|
|
*readPos = pos
|
|
*readVal = val
|
|
return
|
|
}
|
|
|
|
// copied from https://sourcegraph.com/github.com/timtadh/data-structures@master/-/blob/test/support.go
|
|
|
|
type T testing.T
|
|
|
|
func (t *T) Assert(ok bool, msg string, vars ...ItemValue) {
|
|
if !ok {
|
|
t.Log("\n" + string(debug.Stack()))
|
|
var objects []interface{}
|
|
for _, t := range vars {
|
|
objects = append(objects, t)
|
|
}
|
|
t.Fatalf(msg, objects...)
|
|
}
|
|
}
|
|
|
|
func (t *T) AssertNil(errors ...error) {
|
|
any := false
|
|
for _, err := range errors {
|
|
if err != nil {
|
|
any = true
|
|
t.Log("\n" + string(debug.Stack()))
|
|
t.Error(err)
|
|
}
|
|
}
|
|
if any {
|
|
t.Fatal("assert failed")
|
|
}
|
|
}
|
|
|
|
func RandSlice(length int) []byte {
|
|
slice := make([]byte, length)
|
|
if _, err := crand.Read(slice); err != nil {
|
|
panic(err)
|
|
}
|
|
return slice
|
|
}
|
|
|
|
func RandHex(length int) string {
|
|
return hex.EncodeToString(RandSlice(length / 2))
|
|
}
|
|
|
|
func RandStr(length int) string {
|
|
return string(RandSlice(length))
|
|
} |