Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxim Kupriianov committed Jul 19, 2016
0 parents commit fbe8c76
Show file tree
Hide file tree
Showing 15 changed files with 3,198 additions and 0 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# btree-2d

Package `btree-2d` implements a 2-dimensional B+tree data structure.
The B+tree implementation itself is generated by http://github.com/cznic/b.

## Installation

```
go get github.com/zenhotels/btree-2d
```

## Example

None.

## Benchmarks

None.

## License

BSD/MIT
90 changes: 90 additions & 0 deletions btree2d.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package btree2d

import (
"github.com/zenhotels/btree-2d/common"
"github.com/zenhotels/btree-2d/primary"
"github.com/zenhotels/btree-2d/secondary"
)

type BTree2D interface {
Sync(next BTree2D, onAdd, onDel func(key1, key2 common.Comparable))
GetLayer(key1 common.Comparable) (secondary.Layer, bool)
SetLayer(key1 common.Comparable, layer secondary.Layer)
ForEach(fn func(key common.Comparable, layer secondary.Layer) bool)
ForEach2(key1 common.Comparable, fn func(key2 common.Comparable) bool)
Put(key1 common.Comparable, key2 common.FinalizableComparable)
Delete(key1, key2 common.Comparable) bool
Drop(key1 common.Comparable) bool
}

func NewBTree2D() BTree2D {
return btree2d{
primary: primary.NewLayer(),
}
}

type btree2d struct {
primary primary.Layer
}

func (prev btree2d) Sync(next BTree2D, onAdd, onDel func(key1, key2 common.Comparable)) {
nextBTree2D := next.(btree2d)

switch {
case onAdd != nil && onDel != nil:
prev.primary.Sync(nextBTree2D.primary, func(k1 primary.Key, k2 secondary.Key) {
onAdd(k1.Value, k2.Value)
}, func(k1 primary.Key, k2 secondary.Key) {
onDel(k1.Value, k2.Value)
})
case onAdd != nil:
prev.primary.Sync(nextBTree2D.primary, func(k1 primary.Key, k2 secondary.Key) {
onAdd(k1.Value, k2.Value)
}, nil)
case onDel != nil:
prev.primary.Sync(nextBTree2D.primary, nil, func(k1 primary.Key, k2 secondary.Key) {
onDel(k1.Value, k2.Value)
})
default:
prev.primary.Sync(nextBTree2D.primary, nil, nil)
}
}

func (b btree2d) ForEach(fn func(key common.Comparable, layer secondary.Layer) bool) {
b.primary.ForEach(func(key primary.Key, layer secondary.Layer) bool {
return fn(key.Value, layer)
})
}

func (b btree2d) ForEach2(key1 common.Comparable, fn func(key2 common.Comparable) bool) {
if layer2, ok := b.primary.Get(primary.Key{key1}); ok {
layer2.ForEach(func(key secondary.Key) bool {
return fn(key.Value)
})
}
}

func (b btree2d) SetLayer(key1 common.Comparable, layer secondary.Layer) {
b.primary.Set(primary.Key{key1}, layer)
}

func (b btree2d) GetLayer(key1 common.Comparable) (secondary.Layer, bool) {
return b.primary.Get(primary.Key{key1})
}

func (b btree2d) Drop(key1 common.Comparable) bool {
return b.primary.Drop(primary.Key{key1})
}

func (b btree2d) Put(key1 common.Comparable, key2 common.FinalizableComparable) {
b.primary.Put(primary.Key{key1}, secondary.Key{key2})
}

func (b btree2d) Delete(key1, key2 common.Comparable) (ok bool) {
layer2, ok := b.primary.Get(primary.Key{key1})
if !ok {
return false
}
fkey := NewFinalizable(key2)
return layer2.Delete(secondary.Key{fkey})
}
147 changes: 147 additions & 0 deletions btree_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package btree2d

import (
"fmt"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/zenhotels/btree-2d/common"
"github.com/zenhotels/btree-2d/secondary"
)

// coverage: 21.3% of statements
func TestBTree2DSync(t *testing.T) {
assert := assert.New(t)

next := getTree(1000)
var added int
var deleted int
empty := NewBTree2D()
empty.Sync(next, func(_, _ common.Comparable) {
// onAdd
added++
}, func(_, _ common.Comparable) {
// onDel
deleted++
})

assert.Equal(1000*1000, added)
assert.Equal(0, deleted)

var layer1 int
var layer2 int
empty.ForEach(func(_ common.Comparable, layer secondary.Layer) bool {
layer1++
layer.ForEach(func(_ secondary.Key) bool {
layer2++
return false
})
return false
})

assert.Equal(1000, layer1)
assert.Equal(1000*1000, layer2)
}

func getTree(nLimit int, callbacks ...func()) BTree2D {
next := NewBTree2D()
for i := 0; i < nLimit; i++ {
for j := 0; j < nLimit; j++ {
info := &routeInfo{
Host: uint64((i + 1) * (j + 1)),
}
next.Put(ID(i), NewFinalizable(info))
}
}
return next
}

func BenchmarkTreeSync(b *testing.B) {
next := getTree(100, func() {})
b.ResetTimer()
b.ReportAllocs()

var added int
var deleted int
for i := 0; i < b.N; i++ {
empty := NewBTree2D()
empty.Sync(next, func(_, _ common.Comparable) {
// onAdd
added++
}, func(_, _ common.Comparable) {
// onDel
deleted++
})
}

b.StopTimer()
if added != 10000*b.N {
b.Fatal("wrong added count", added)
}
}

// func BenchmarkRegistrySync(b *testing.B) {
// next := getRegistry(100, func() {})
// b.ResetTimer()
// b.ReportAllocs()

// var added int
// var deleted int
// for i := 0; i < b.N; i++ {
// var empty route.Registry
// empty.Sync(next, func(_ uint64, _ route.RouteInfo) {
// // onAdd
// added++
// }, func(_ uint64, _ route.RouteInfo) {
// // onDel
// deleted++
// })
// }

// b.StopTimer()
// if added != 10000*b.N {
// b.Fatal("wrong added count", added)
// }
// }

// func getRegistry(nLimit int, callbacks ...func()) *route.Registry {
// next := new(route.Registry)
// for i := 0; i < nLimit; i++ {
// for j := 0; j < nLimit; j++ {
// info := route.RouteInfo{
// Host: uint64((i + 1) * (j + 1)),
// }
// next.Push(uint64(i), info)
// }
// }
// return next
// }

type ID uint64

func (u ID) Less(u2 common.Comparable) bool {
return u < u2.(ID)
}

type routeInfo struct {
Host uint64
Distance int
Upstream *http.Transport
}

func (r routeInfo) String() string {
return fmt.Sprintf("{%d<-%s:%d}", r.Host, r.Upstream, r.Distance)
}

func (r routeInfo) Less(r2 common.Comparable) bool {
return r.Host < r2.(*routeInfo).Host
}

// func (r *routeInfo) AddFinalizer(fn func()) bool {
// return false
// }

// func (r *routeInfo) Finalize() {

// }
12 changes: 12 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package common

type Comparable interface {
Less(other Comparable) bool
}

type FinalizableComparable interface {
Comparable

Finalize()
AddFinalizer(fn func()) bool
}
77 changes: 77 additions & 0 deletions finalizers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package btree2d

import (
"sync"
"sync/atomic"

"github.com/zenhotels/btree-2d/common"
)

const MaxFinalizers = 16

func NewFinalizable(obj common.Comparable) common.FinalizableComparable {
return &withFinalizers{
obj: obj,
}
}

func NonFinalizable(obj common.Comparable) common.FinalizableComparable {
return &withFinalizers{
obj: obj,
}
}

type withoutFinalizers struct {
obj common.Comparable
}

func (w *withoutFinalizers) Less(w2 common.Comparable) bool {
return w.obj.Less(w2.(*withoutFinalizers).obj)
}

func (w *withoutFinalizers) Finalize() {

}

func (w *withoutFinalizers) AddFinalizer(fn func()) bool {
return false
}

type withFinalizers struct {
obj common.Comparable

offset int64
list []func()
onceNewList sync.Once
}

func (w *withFinalizers) Less(w2 common.Comparable) bool {
return w.obj.Less(w2.(*withFinalizers).obj)
}

func (w *withFinalizers) Finalize() {
offset := atomic.SwapInt64(&w.offset, 100)
if offset >= 100 {
return
} else if offset > MaxFinalizers {
offset = MaxFinalizers
}
_ = w.list[MaxFinalizers-1]
for i := int64(0); i < offset; i++ {
go w.list[i]()
}
w.list = nil // protected by swapInt64
}

func (w *withFinalizers) AddFinalizer(fn func()) bool {
w.onceNewList.Do(func() {
w.list = make([]func(), MaxFinalizers)
})
offset := atomic.AddInt64(&w.offset, 1)
if offset > MaxFinalizers {
atomic.AddInt64(&w.offset, -1)
return false
}
w.list[offset-1] = fn
return true
}
21 changes: 21 additions & 0 deletions lockie/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2016 Meteora

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading

0 comments on commit fbe8c76

Please # to comment.