upgrade buntdb and dependencies

This commit is contained in:
Shivaram Lingamneni
2023-01-15 08:26:32 -05:00
parent b2087977d0
commit 3f5de80afd
12 changed files with 2078 additions and 419 deletions

View File

@@ -2,26 +2,351 @@
[![GoDoc](https://godoc.org/github.com/tidwall/btree?status.svg)](https://godoc.org/github.com/tidwall/btree)
An [efficient](#performance) [B-tree](https://en.wikipedia.org/wiki/B-tree) implementation in Go.
*Check out the [generics branch](https://github.com/tidwall/btree/tree/generics) if you want to try out btree with generic support for Go 1.18+*
An efficient [B-tree](https://en.wikipedia.org/wiki/B-tree) implementation in Go.
## Features
- `Copy()` method with copy-on-write support.
- Support for [Generics](#generics) (Go 1.18+).
- `Map` and `Set` types for ordered key-value maps and sets,
- Fast bulk loading for pre-ordered data using the `Load()` method.
- All operations are thread-safe.
- `Copy()` method with copy-on-write support.
- Thread-safe operations.
- [Path hinting](PATH_HINT.md) optimization for operations with nearby keys.
## Installing
## Using
To start using btree, install Go and run `go get`:
To start using this package, install Go and run:
```sh
$ go get -u github.com/tidwall/btree
$ go get github.com/tidwall/btree
```
## Usage
## B-tree types
This package includes the following types of B-trees:
- [`btree.Map`](#btreemap):
A fast B-tree for storing ordered key value pairs.
Go 1.18+
- [`btree.Set`](#btreeset):
Like `Map`, but only for storing keys.
Go 1.18+
- [`btree.BTreeG`](#btreegeneric):
A feature-rich B-tree for storing data using a custom comparator.
Go 1.18+
- [`btree.BTree`](#btreebtree):
Like `BTreeG` but uses the `interface{}` type for data. Backwards compatible.
Go 1.16+
### btree.Map
```go
// Basic
Set(key, value) // insert or replace an item
Get(key, value) // get an existing item
Delete(key) // delete an item
Len() // return the number of items in the map
// Iteration
Scan(iter) // scan items in ascending order
Reverse(iter) // scan items in descending order
Ascend(key, iter) // scan items in ascending order that are >= to key
Descend(key, iter) // scan items in descending order that are <= to key.
Iter() // returns a read-only iterator for for-loops.
// Array-like operations
GetAt(index) // returns the item at index
DeleteAt(index) // deletes the item at index
// Bulk-loading
Load(key, value) // load presorted items into tree
```
#### Example
```go
package main
import (
"fmt"
"github.com/tidwall/btree"
)
func main() {
// create a map
var users btree.Map[string, string]
// add some users
users.Set("user:4", "Andrea")
users.Set("user:6", "Andy")
users.Set("user:2", "Andy")
users.Set("user:1", "Jane")
users.Set("user:5", "Janet")
users.Set("user:3", "Steve")
// Iterate over the maps and print each user
users.Scan(func(key, value string) bool {
fmt.Printf("%s %s\n", key, value)
return true
})
fmt.Printf("\n")
// Delete a couple
users.Delete("user:5")
users.Delete("user:1")
// print the map again
users.Scan(func(key, value string) bool {
fmt.Printf("%s %s\n", key, value)
return true
})
fmt.Printf("\n")
// Output:
// user:1 Jane
// user:2 Andy
// user:3 Steve
// user:4 Andrea
// user:5 Janet
// user:6 Andy
//
// user:2 Andy
// user:3 Steve
// user:4 Andrea
// user:6 Andy
}
```
### btree.Set
```go
// Basic
Insert(key) // insert an item
Contains(key) // test if item exists
Delete(key) // delete an item
Len() // return the number of items in the set
// Iteration
Scan(iter) // scan items in ascending order
Reverse(iter) // scan items in descending order
Ascend(key, iter) // scan items in ascending order that are >= to key
Descend(key, iter) // scan items in descending order that are <= to key.
Iter() // returns a read-only iterator for for-loops.
// Array-like operations
GetAt(index) // returns the item at index
DeleteAt(index) // deletes the item at index
// Bulk-loading
Load(key) // load presorted item into tree
```
#### Example
```go
package main
import (
"fmt"
"github.com/tidwall/btree"
)
func main() {
// create a set
var names btree.Set[string]
// add some names
names.Insert("Jane")
names.Insert("Andrea")
names.Insert("Steve")
names.Insert("Andy")
names.Insert("Janet")
names.Insert("Andy")
// Iterate over the maps and print each user
names.Scan(func(key string) bool {
fmt.Printf("%s\n", key)
return true
})
fmt.Printf("\n")
// Delete a couple
names.Delete("Steve")
names.Delete("Andy")
// print the map again
names.Scan(func(key string) bool {
fmt.Printf("%s\n", key)
return true
})
fmt.Printf("\n")
// Output:
// Andrea
// Andy
// Jane
// Janet
// Steve
//
// Andrea
// Jane
// Janet
}
```
### btree.BTreeG
```go
// Basic
Set(item) // insert or replace an item
Get(item) // get an existing item
Delete(item) // delete an item
Len() // return the number of items in the btree
// Iteration
Scan(iter) // scan items in ascending order
Reverse(iter) // scan items in descending order
Ascend(key, iter) // scan items in ascending order that are >= to key
Descend(key, iter) // scan items in descending order that are <= to key.
Iter() // returns a read-only iterator for for-loops.
// Array-like operations
GetAt(index) // returns the item at index
DeleteAt(index) // deletes the item at index
// Bulk-loading
Load(item) // load presorted items into tree
// Path hinting
SetHint(item, *hint) // insert or replace an existing item
GetHint(item, *hint) // get an existing item
DeleteHint(item, *hint) // delete an item
// Copy-on-write
Copy() // copy the btree
```
#### Example
```go
package main
import (
"fmt"
"github.com/tidwall/btree"
)
type Item struct {
Key, Val string
}
// byKeys is a comparison function that compares item keys and returns true
// when a is less than b.
func byKeys(a, b Item) bool {
return a.Key < b.Key
}
// byVals is a comparison function that compares item values and returns true
// when a is less than b.
func byVals(a, b Item) bool {
if a.Val < b.Val {
return true
}
if a.Val > b.Val {
return false
}
// Both vals are equal so we should fall though
// and let the key comparison take over.
return byKeys(a, b)
}
func main() {
// Create a tree for keys and a tree for values.
// The "keys" tree will be sorted on the Keys field.
// The "values" tree will be sorted on the Values field.
keys := btree.NewBTreeG[Item](byKeys)
vals := btree.NewBTreeG[Item](byVals)
// Create some items.
users := []Item{
Item{Key: "user:1", Val: "Jane"},
Item{Key: "user:2", Val: "Andy"},
Item{Key: "user:3", Val: "Steve"},
Item{Key: "user:4", Val: "Andrea"},
Item{Key: "user:5", Val: "Janet"},
Item{Key: "user:6", Val: "Andy"},
}
// Insert each user into both trees
for _, user := range users {
keys.Set(user)
vals.Set(user)
}
// Iterate over each user in the key tree
keys.Scan(func(item Item) bool {
fmt.Printf("%s %s\n", item.Key, item.Val)
return true
})
fmt.Printf("\n")
// Iterate over each user in the val tree
vals.Scan(func(item Item) bool {
fmt.Printf("%s %s\n", item.Key, item.Val)
return true
})
// Output:
// user:1 Jane
// user:2 Andy
// user:3 Steve
// user:4 Andrea
// user:5 Janet
// user:6 Andy
//
// user:4 Andrea
// user:2 Andy
// user:6 Andy
// user:1 Jane
// user:5 Janet
// user:3 Steve
}
```
### btree.BTree
```go
// Basic
Set(item) // insert or replace an item
Get(item) // get an existing item
Delete(item) // delete an item
Len() // return the number of items in the btree
// Iteration
Scan(iter) // scan items in ascending order
Reverse(iter) // scan items in descending order
Ascend(key, iter) // scan items in ascending order that are >= to key
Descend(key, iter) // scan items in descending order that are <= to key.
Iter() // returns a read-only iterator for for-loops.
// Array-like operations
GetAt(index) // returns the item at index
DeleteAt(index) // deletes the item at index
// Bulk-loading
Load(item) // load presorted items into tree
// Path hinting
SetHint(item, *hint) // insert or replace an existing item
GetHint(item, *hint) // get an existing item
DeleteHint(item, *hint) // delete an item
// Copy-on-write
Copy() // copy the btree
```
#### Example
```go
package main
@@ -113,92 +438,9 @@ func main() {
}
```
## Operations
### Basic
```
Get(item) # get an existing item
Set(item) # insert or replace an existing item
Delete(item) # delete an item
Len() # return the number of items in the btree
```
### Iteration
```
Ascend(pivot, iter) # scan items in ascending order starting at pivot.
Descend(pivot, iter) # scan items in descending order starting at pivot.
Iter() # returns a read-only iterator for for-loops.
```
### Queues
```
Min() # return the first item in the btree
Max() # return the last item in the btree
PopMin() # remove and return the first item in the btree
PopMax() # remove and return the last item in the btree
```
### Bulk loading
```
Load(item) # load presorted items into tree
```
### Path hints
```
SetHint(item, *hint) # insert or replace an existing item
GetHint(item, *hint) # get an existing item
DeleteHint(item, *hint) # delete an item
```
### Array-like operations
```
GetAt(index) # returns the value at index
DeleteAt(index) # deletes the item at index
```
## Performance
This implementation was designed with performance in mind.
The following benchmarks were run on my 2019 Macbook Pro (2.4 GHz 8-Core Intel Core i9) using Go 1.17.3. The items are simple 8-byte ints.
- `google`: The [google/btree](https://github.com/google/btree) package
- `tidwall`: The [tidwall/btree](https://github.com/tidwall/btree) package
- `go-arr`: Just a simple Go array
```
** sequential set **
google: set-seq 1,000,000 ops in 178ms, 5,618,049/sec, 177 ns/op, 39.0 MB, 40 bytes/op
tidwall: set-seq 1,000,000 ops in 156ms, 6,389,837/sec, 156 ns/op, 23.5 MB, 24 bytes/op
tidwall: set-seq-hint 1,000,000 ops in 78ms, 12,895,355/sec, 77 ns/op, 23.5 MB, 24 bytes/op
tidwall: load-seq 1,000,000 ops in 53ms, 18,937,400/sec, 52 ns/op, 23.5 MB, 24 bytes/op
go-arr: append 1,000,000 ops in 78ms, 12,843,432/sec, 77 ns/op
** random set **
google: set-rand 1,000,000 ops in 555ms, 1,803,133/sec, 554 ns/op, 29.7 MB, 31 bytes/op
tidwall: set-rand 1,000,000 ops in 545ms, 1,835,818/sec, 544 ns/op, 29.6 MB, 31 bytes/op
tidwall: set-rand-hint 1,000,000 ops in 670ms, 1,493,473/sec, 669 ns/op, 29.6 MB, 31 bytes/op
tidwall: set-again 1,000,000 ops in 681ms, 1,469,038/sec, 680 ns/op
tidwall: set-after-copy 1,000,000 ops in 670ms, 1,493,230/sec, 669 ns/op
tidwall: load-rand 1,000,000 ops in 569ms, 1,756,187/sec, 569 ns/op, 29.6 MB, 31 bytes/op
** sequential get **
google: get-seq 1,000,000 ops in 165ms, 6,048,307/sec, 165 ns/op
tidwall: get-seq 1,000,000 ops in 144ms, 6,940,120/sec, 144 ns/op
tidwall: get-seq-hint 1,000,000 ops in 78ms, 12,815,243/sec, 78 ns/op
** random get **
google: get-rand 1,000,000 ops in 701ms, 1,427,507/sec, 700 ns/op
tidwall: get-rand 1,000,000 ops in 679ms, 1,473,531/sec, 678 ns/op
tidwall: get-rand-hint 1,000,000 ops in 824ms, 1,213,805/sec, 823 ns/op
```
*You can find the benchmark utility at [tidwall/btree-benchmark](https://github.com/tidwall/btree-benchmark)*
See [tidwall/btree-benchmark](https://github.com/tidwall/btree-benchmark) for benchmark numbers.
## Contact

View File

@@ -3,25 +3,17 @@
// license that can be found in the LICENSE file.
package btree
import btree "github.com/tidwall/btree/internal"
type BTree struct {
base *btree.BTree
base *BTreeG[any]
}
// PathHint is a utility type used with the *Hint() functions. Hints provide
// faster operations for clustered keys.
type PathHint = btree.PathHint
// New returns a new BTree
func New(less func(a, b interface{}) bool) *BTree {
func New(less func(a, b any) bool) *BTree {
if less == nil {
panic("nil less")
}
return &BTree{
base: btree.NewOptions(btree.Options{
Context: less,
}),
base: NewBTreeG(less),
}
}
@@ -30,31 +22,33 @@ func New(less func(a, b interface{}) bool) *BTree {
//
// This is useful for when you do not need the BTree to manage the locking,
// but would rather do it yourself.
func NewNonConcurrent(less func(a, b interface{}) bool) *BTree {
func NewNonConcurrent(less func(a, b any) bool) *BTree {
if less == nil {
panic("nil less")
}
return &BTree{
base: btree.NewOptions(btree.Options{
Context: less,
NoLocks: true,
}),
base: NewBTreeGOptions(less,
Options{
NoLocks: true,
}),
}
}
// Less is a convenience function that performs a comparison of two items
// using the same "less" function provided to New.
func (tr *BTree) Less(a, b interface{}) bool {
func (tr *BTree) Less(a, b any) bool {
return tr.base.Less(a, b)
}
// Set or replace a value for a key
func (tr *BTree) Set(item interface{}) interface{} {
// Returns the value for the replaced item or nil if the key was not found.
func (tr *BTree) Set(item any) (prev any) {
return tr.SetHint(item, nil)
}
// SetHint sets or replace a value for a key using a path hint
func (tr *BTree) SetHint(item interface{}, hint *PathHint) (prev interface{}) {
// Returns the value for the replaced item or nil if the key was not found.
func (tr *BTree) SetHint(item any, hint *PathHint) (prev any) {
if item == nil {
panic("nil item")
}
@@ -65,13 +59,15 @@ func (tr *BTree) SetHint(item interface{}, hint *PathHint) (prev interface{}) {
return v
}
// Get a value for key
func (tr *BTree) Get(key interface{}) interface{} {
// Get a value for key.
// Returns nil if the key was not found.
func (tr *BTree) Get(key any) any {
return tr.GetHint(key, nil)
}
// GetHint gets a value for key using a path hint
func (tr *BTree) GetHint(key interface{}, hint *PathHint) interface{} {
// GetHint gets a value for key using a path hint.
// Returns nil if the item was not found.
func (tr *BTree) GetHint(key any, hint *PathHint) (value any) {
if key == nil {
return nil
}
@@ -87,13 +83,15 @@ func (tr *BTree) Len() int {
return tr.base.Len()
}
// Delete a value for a key
func (tr *BTree) Delete(key interface{}) interface{} {
// Delete an item for a key.
// Returns the deleted value or nil if the key was not found.
func (tr *BTree) Delete(key any) (prev any) {
return tr.DeleteHint(key, nil)
}
// DeleteHint deletes a value for a key using a path hint
func (tr *BTree) DeleteHint(key interface{}, hint *PathHint) interface{} {
// Returns the deleted value or nil if the key was not found.
func (tr *BTree) DeleteHint(key any, hint *PathHint) (prev any) {
if key == nil {
return nil
}
@@ -107,7 +105,7 @@ func (tr *BTree) DeleteHint(key interface{}, hint *PathHint) interface{} {
// Ascend the tree within the range [pivot, last]
// Pass nil for pivot to scan all item in ascending order
// Return false to stop iterating
func (tr *BTree) Ascend(pivot interface{}, iter func(item interface{}) bool) {
func (tr *BTree) Ascend(pivot any, iter func(item any) bool) {
if pivot == nil {
tr.base.Scan(iter)
} else {
@@ -118,7 +116,7 @@ func (tr *BTree) Ascend(pivot interface{}, iter func(item interface{}) bool) {
// Descend the tree within the range [pivot, first]
// Pass nil for pivot to scan all item in descending order
// Return false to stop iterating
func (tr *BTree) Descend(pivot interface{}, iter func(item interface{}) bool) {
func (tr *BTree) Descend(pivot any, iter func(item any) bool) {
if pivot == nil {
tr.base.Reverse(iter)
} else {
@@ -127,7 +125,9 @@ func (tr *BTree) Descend(pivot interface{}, iter func(item interface{}) bool) {
}
// Load is for bulk loading pre-sorted items
func (tr *BTree) Load(item interface{}) interface{} {
// If the load replaces and existing item then the value for the replaced item
// is returned.
func (tr *BTree) Load(item any) (prev any) {
if item == nil {
panic("nil item")
}
@@ -140,7 +140,7 @@ func (tr *BTree) Load(item interface{}) interface{} {
// Min returns the minimum item in tree.
// Returns nil if the tree has no items.
func (tr *BTree) Min() interface{} {
func (tr *BTree) Min() any {
v, ok := tr.base.Min()
if !ok {
return nil
@@ -150,7 +150,7 @@ func (tr *BTree) Min() interface{} {
// Max returns the maximum item in tree.
// Returns nil if the tree has no items.
func (tr *BTree) Max() interface{} {
func (tr *BTree) Max() any {
v, ok := tr.base.Max()
if !ok {
return nil
@@ -160,7 +160,7 @@ func (tr *BTree) Max() interface{} {
// PopMin removes the minimum item in tree and returns it.
// Returns nil if the tree has no items.
func (tr *BTree) PopMin() interface{} {
func (tr *BTree) PopMin() any {
v, ok := tr.base.PopMin()
if !ok {
return nil
@@ -168,9 +168,9 @@ func (tr *BTree) PopMin() interface{} {
return v
}
// PopMax removes the minimum item in tree and returns it.
// PopMax removes the maximum item in tree and returns it.
// Returns nil if the tree has no items.
func (tr *BTree) PopMax() interface{} {
func (tr *BTree) PopMax() any {
v, ok := tr.base.PopMax()
if !ok {
return nil
@@ -180,7 +180,7 @@ func (tr *BTree) PopMax() interface{} {
// GetAt returns the value at index.
// Return nil if the tree is empty or the index is out of bounds.
func (tr *BTree) GetAt(index int) interface{} {
func (tr *BTree) GetAt(index int) any {
v, ok := tr.base.GetAt(index)
if !ok {
return nil
@@ -190,7 +190,7 @@ func (tr *BTree) GetAt(index int) interface{} {
// DeleteAt deletes the item at index.
// Return nil if the tree is empty or the index is out of bounds.
func (tr *BTree) DeleteAt(index int) interface{} {
func (tr *BTree) DeleteAt(index int) any {
v, ok := tr.base.DeleteAt(index)
if !ok {
return nil
@@ -206,8 +206,8 @@ func (tr *BTree) Height() int {
// Walk iterates over all items in tree, in order.
// The items param will contain one or more items.
func (tr *BTree) Walk(iter func(items []interface{})) {
tr.base.Walk(func(items []interface{}) bool {
func (tr *BTree) Walk(iter func(items []any)) {
tr.base.Walk(func(items []any) bool {
iter(items)
return true
})
@@ -220,7 +220,7 @@ func (tr *BTree) Copy() *BTree {
}
type Iter struct {
base btree.Iter
base GenericIter[any]
}
// Iter returns a read-only iterator.
@@ -231,7 +231,7 @@ func (tr *BTree) Iter() Iter {
// Seek to item greater-or-equal-to key.
// Returns false if there was no item found.
func (iter *Iter) Seek(key interface{}) bool {
func (iter *Iter) Seek(key any) bool {
return iter.base.Seek(key)
}
@@ -268,6 +268,6 @@ func (iter *Iter) Prev() bool {
}
// Item returns the current iterator item.
func (iter *Iter) Item() interface{} {
func (iter *Iter) Item() any {
return iter.base.Item()
}

View File

@@ -1,178 +1,109 @@
// Copyright 2020 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file at https://github.com/tidwall/btree/LICENSE
///////////////////////////////////////////////////////////////////////////////
// BEGIN PARAMS
///////////////////////////////////////////////////////////////////////////////
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package btree
import "sync"
import (
"sync"
"sync/atomic"
)
// degree is the B-Tree degree, which is equal to maximum number of children
// pre node times two.
// The default is 128, which means each node can have 255 items and 256 child
// nodes.
const degree = 128
const (
degree = 128
maxItems = degree*2 - 1 // max items per node. max children is +1
minItems = maxItems / 2
)
// kind is the item type.
// It's important to use the equal symbol, which tells Go to create an alias of
// the type, rather than creating an entirely new type.
type kind = interface{}
// contextKind is the kind of context that can be passed to NewOptions and the
// less function
type contextKind = interface{}
// less returns true if A is less than B.
// The value of context will be whatever was passed to NewOptions through the
// Options.Context field, otherwise nil if the field was not set.
func less(a, b kind, context contextKind) bool {
return context.(func(a, b contextKind) bool)(a, b)
}
// BTree aliases
// These are aliases to the local bTree types and functions, which are exported
// to allow for public use at a package level.
// Rename them if desired, or comment them out to make the library private.
type BTree = bTree
type Options = bOptions
type PathHint = bPathHint
type Iter = bIter
func New(less func(a, b kind) bool) *bTree { return bNew() }
func NewOptions(opts bOptions) *bTree { return bNewOptions(opts) }
// The functions below, which begin with "test*", are required by the
// btree_test.go file. If you choose not use include the btree_test.go file in
// your project then these functions may be omitted.
// testCustomSeed can be used to generate a custom random seed for testing.
// Returning false will use time.Now().UnixNano()
func testCustomSeed() (seed int64, ok bool) {
return 0, false
}
// testMakeItem must return a valid item for testing.
// It's required that the returned item maintains equal order as the
// provided int, such that:
// testMakeItem(0) < testMakeItem(1) < testMakeItem(2) < testMakeItem(10)
func testMakeItem(x int) (item kind) {
return x
}
// testNewBTree must return an operational btree for testing.
func testNewBTree() *bTree {
return bNewOptions(bOptions{
Context: func(a, b contextKind) bool {
if a == nil {
return b != nil
} else if b == nil {
return false
}
return a.(int) < b.(int)
},
})
}
///////////////////////////////////////////////////////////////////////////////
// END PARAMS
///////////////////////////////////////////////////////////////////////////////
// Do not edit code below this line.
const maxItems = degree*2 - 1 // max items per node. max children is +1
const minItems = maxItems / 2
type bTree struct {
type BTreeG[T any] struct {
mu *sync.RWMutex
cow *cow
root *node
cow uint64
root *node[T]
count int
ctx contextKind
locks bool
empty kind
less func(a, b T) bool
empty T
}
type node struct {
cow *cow
type node[T any] struct {
cow uint64
count int
items []kind
children *[]*node
items []T
children *[]*node[T]
}
type cow struct {
_ int // cannot be an empty struct
}
func (tr *bTree) newNode(leaf bool) *node {
n := &node{cow: tr.cow}
if !leaf {
n.children = new([]*node)
}
return n
}
// leaf returns true if the node is a leaf.
func (n *node) leaf() bool {
return n.children == nil
}
var gcow uint64
// PathHint is a utility type used with the *Hint() functions. Hints provide
// faster operations for clustered keys.
type bPathHint struct {
type PathHint struct {
used [8]bool
path [8]uint8
}
type bOptions struct {
// Options for passing to New when creating a new BTree.
type Options struct {
NoLocks bool
Context contextKind
}
// New returns a new BTree
func bNew() *bTree {
return bNewOptions(bOptions{})
func NewBTreeG[T any](less func(a, b T) bool) *BTreeG[T] {
return NewBTreeGOptions(less, Options{})
}
func bNewOptions(opts bOptions) *bTree {
tr := new(bTree)
tr.cow = new(cow)
func NewBTreeGOptions[T any](less func(a, b T) bool, opts Options) *BTreeG[T] {
tr := new(BTreeG[T])
tr.cow = atomic.AddUint64(&gcow, 1)
tr.mu = new(sync.RWMutex)
tr.ctx = opts.Context
tr.less = less
tr.locks = !opts.NoLocks
return tr
}
// Less is a convenience function that performs a comparison of two items
// using the same "less" function provided to New.
func (tr *bTree) Less(a, b kind) bool {
return less(a, b, tr.ctx)
func (tr *BTreeG[T]) Less(a, b T) bool {
return tr.less(a, b)
}
func (tr *bTree) find(n *node, key kind,
hint *bPathHint, depth int,
func (tr *BTreeG[T]) newNode(leaf bool) *node[T] {
n := &node[T]{cow: tr.cow}
if !leaf {
n.children = new([]*node[T])
}
return n
}
// leaf returns true if the node is a leaf.
func (n *node[T]) leaf() bool {
return n.children == nil
}
func (tr *BTreeG[T]) bsearch(n *node[T], key T) (index int, found bool) {
low, high := 0, len(n.items)
for low < high {
h := int(uint(low+high) >> 1)
if !tr.less(key, n.items[h]) {
low = h + 1
} else {
high = h
}
}
if low > 0 && !tr.less(n.items[low-1], key) {
return low - 1, true
}
return low, false
}
func (tr *BTreeG[T]) find(n *node[T], key T, hint *PathHint, depth int,
) (index int, found bool) {
if hint == nil {
// fast path for no hinting
low := 0
high := len(n.items)
for low < high {
mid := (low + high) / 2
if !tr.Less(key, n.items[mid]) {
low = mid + 1
} else {
high = mid
}
}
if low > 0 && !tr.Less(n.items[low-1], key) {
return low - 1, true
}
return low, false
return tr.bsearch(n, key)
}
return tr.hintsearch(n, key, hint, depth)
}
// Try using hint.
func (tr *BTreeG[T]) hintsearch(n *node[T], key T, hint *PathHint, depth int,
) (index int, found bool) {
// Best case finds the exact match, updates the hint and returns.
// Worst case, updates the low and high bounds to binary search between.
low := 0
@@ -247,17 +178,21 @@ path_match:
}
// SetHint sets or replace a value for a key using a path hint
func (tr *bTree) SetHint(item kind, hint *bPathHint) (prev kind, replaced bool) {
if tr.lock() {
defer tr.unlock()
func (tr *BTreeG[T]) SetHint(item T, hint *PathHint) (prev T, replaced bool) {
if tr.locks {
tr.mu.Lock()
prev, replaced = tr.setHint(item, hint)
tr.mu.Unlock()
} else {
prev, replaced = tr.setHint(item, hint)
}
return tr.setHint(item, hint)
return prev, replaced
}
func (tr *bTree) setHint(item kind, hint *bPathHint) (prev kind, replaced bool) {
func (tr *BTreeG[T]) setHint(item T, hint *PathHint) (prev T, replaced bool) {
if tr.root == nil {
tr.root = tr.newNode(true)
tr.root.items = append([]kind{}, item)
tr.root.items = append([]T{}, item)
tr.root.count = 1
tr.count = 1
return tr.empty, false
@@ -267,9 +202,9 @@ func (tr *bTree) setHint(item kind, hint *bPathHint) (prev kind, replaced bool)
left := tr.cowLoad(&tr.root)
right, median := tr.nodeSplit(left)
tr.root = tr.newNode(false)
*tr.root.children = make([]*node, 0, maxItems+1)
*tr.root.children = append([]*node{}, left, right)
tr.root.items = append([]kind{}, median)
*tr.root.children = make([]*node[T], 0, maxItems+1)
*tr.root.children = append([]*node[T]{}, left, right)
tr.root.items = append([]T{}, median)
tr.root.updateCount()
return tr.setHint(item, hint)
}
@@ -281,39 +216,61 @@ func (tr *bTree) setHint(item kind, hint *bPathHint) (prev kind, replaced bool)
}
// Set or replace a value for a key
func (tr *bTree) Set(item kind) (kind, bool) {
func (tr *BTreeG[T]) Set(item T) (T, bool) {
return tr.SetHint(item, nil)
}
func (tr *bTree) nodeSplit(n *node) (right *node, median kind) {
func (tr *BTreeG[T]) nodeSplit(n *node[T]) (right *node[T], median T) {
i := maxItems / 2
median = n.items[i]
// left node
left := tr.newNode(n.leaf())
left.items = make([]kind, len(n.items[:i]), maxItems/2)
copy(left.items, n.items[:i])
if !n.leaf() {
*left.children = make([]*node, len((*n.children)[:i+1]), maxItems+1)
copy(*left.children, (*n.children)[:i+1])
}
left.updateCount()
const sliceItems = true
// right node
right = tr.newNode(n.leaf())
right.items = make([]kind, len(n.items[i+1:]), maxItems/2)
copy(right.items, n.items[i+1:])
if !n.leaf() {
*right.children = make([]*node, len((*n.children)[i+1:]), maxItems+1)
copy(*right.children, (*n.children)[i+1:])
if sliceItems {
right.items = n.items[i+1:]
if !n.leaf() {
*right.children = (*n.children)[i+1:]
}
} else {
right.items = make([]T, len(n.items[i+1:]), maxItems/2)
copy(right.items, n.items[i+1:])
if !n.leaf() {
*right.children =
make([]*node[T], len((*n.children)[i+1:]), maxItems+1)
copy(*right.children, (*n.children)[i+1:])
}
}
right.updateCount()
*n = *left
// left node
if sliceItems {
n.items[i] = tr.empty
n.items = n.items[:i:i]
if !n.leaf() {
*n.children = (*n.children)[: i+1 : i+1]
}
} else {
for j := i; j < len(n.items); j++ {
n.items[j] = tr.empty
}
if !n.leaf() {
for j := i + 1; j < len((*n.children)); j++ {
(*n.children)[j] = nil
}
}
n.items = n.items[:i]
if !n.leaf() {
*n.children = (*n.children)[:i+1]
}
}
n.updateCount()
return right, median
}
func (n *node) updateCount() {
func (n *node[T]) updateCount() {
n.count = len(n.items)
if !n.leaf() {
for i := 0; i < len(*n.children); i++ {
@@ -326,33 +283,42 @@ func (n *node) updateCount() {
// called outside of heavy copy-on-write situations. Marking it "noinline"
// allows for the parent cowLoad to be inlined.
// go:noinline
func (tr *bTree) copy(n *node) *node {
n2 := new(node)
func (tr *BTreeG[T]) copy(n *node[T]) *node[T] {
n2 := new(node[T])
n2.cow = tr.cow
n2.count = n.count
n2.items = make([]kind, len(n.items), cap(n.items))
n2.items = make([]T, len(n.items), cap(n.items))
copy(n2.items, n.items)
if !n.leaf() {
n2.children = new([]*node)
*n2.children = make([]*node, len(*n.children), maxItems+1)
n2.children = new([]*node[T])
*n2.children = make([]*node[T], len(*n.children), maxItems+1)
copy(*n2.children, *n.children)
}
return n2
}
// cowLoad loads the provided node and, if needed, performs a copy-on-write.
func (tr *bTree) cowLoad(cn **node) *node {
func (tr *BTreeG[T]) cowLoad(cn **node[T]) *node[T] {
if (*cn).cow != tr.cow {
*cn = tr.copy(*cn)
}
return *cn
}
func (tr *bTree) nodeSet(cn **node, item kind,
hint *bPathHint, depth int,
) (prev kind, replaced bool, split bool) {
n := tr.cowLoad(cn)
i, found := tr.find(n, item, hint, depth)
func (tr *BTreeG[T]) nodeSet(cn **node[T], item T,
hint *PathHint, depth int,
) (prev T, replaced bool, split bool) {
if (*cn).cow != tr.cow {
*cn = tr.copy(*cn)
}
n := *cn
var i int
var found bool
if hint == nil {
i, found = tr.bsearch(n, item)
} else {
i, found = tr.hintsearch(n, item, hint, depth)
}
if found {
prev = n.items[i]
n.items[i] = item
@@ -388,7 +354,7 @@ func (tr *bTree) nodeSet(cn **node, item kind,
return prev, replaced, false
}
func (tr *bTree) Scan(iter func(item kind) bool) {
func (tr *BTreeG[T]) Scan(iter func(item T) bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -398,7 +364,7 @@ func (tr *bTree) Scan(iter func(item kind) bool) {
tr.root.scan(iter)
}
func (n *node) scan(iter func(item kind) bool) bool {
func (n *node[T]) scan(iter func(item T) bool) bool {
if n.leaf() {
for i := 0; i < len(n.items); i++ {
if !iter(n.items[i]) {
@@ -419,15 +385,36 @@ func (n *node) scan(iter func(item kind) bool) bool {
}
// Get a value for key
func (tr *bTree) Get(key kind) (kind, bool) {
return tr.GetHint(key, nil)
func (tr *BTreeG[T]) Get(key T) (T, bool) {
if tr.locks {
return tr.GetHint(key, nil)
}
if tr.root == nil {
return tr.empty, false
}
n := tr.root
for {
i, found := tr.bsearch(n, key)
if found {
return n.items[i], true
}
if n.children == nil {
return tr.empty, false
}
n = (*n.children)[i]
}
}
// GetHint gets a value for key using a path hint
func (tr *bTree) GetHint(key kind, hint *bPathHint) (kind, bool) {
func (tr *BTreeG[T]) GetHint(key T, hint *PathHint) (value T, ok bool) {
if tr.rlock() {
defer tr.runlock()
}
return tr.getHint(key, hint)
}
// GetHint gets a value for key using a path hint
func (tr *BTreeG[T]) getHint(key T, hint *PathHint) (T, bool) {
if tr.root == nil {
return tr.empty, false
}
@@ -447,24 +434,27 @@ func (tr *bTree) GetHint(key kind, hint *bPathHint) (kind, bool) {
}
// Len returns the number of items in the tree
func (tr *bTree) Len() int {
func (tr *BTreeG[T]) Len() int {
return tr.count
}
// Delete a value for a key
func (tr *bTree) Delete(key kind) (kind, bool) {
// Delete a value for a key and returns the deleted value.
// Returns false if there was no value by that key found.
func (tr *BTreeG[T]) Delete(key T) (T, bool) {
return tr.DeleteHint(key, nil)
}
// DeleteHint deletes a value for a key using a path hint
func (tr *bTree) DeleteHint(key kind, hint *bPathHint) (kind, bool) {
// DeleteHint deletes a value for a key using a path hint and returns the
// deleted value.
// Returns false if there was no value by that key found.
func (tr *BTreeG[T]) DeleteHint(key T, hint *PathHint) (T, bool) {
if tr.lock() {
defer tr.unlock()
}
return tr.deleteHint(key, hint)
}
func (tr *bTree) deleteHint(key kind, hint *bPathHint) (kind, bool) {
func (tr *BTreeG[T]) deleteHint(key T, hint *PathHint) (T, bool) {
if tr.root == nil {
return tr.empty, false
}
@@ -482,9 +472,9 @@ func (tr *bTree) deleteHint(key kind, hint *bPathHint) (kind, bool) {
return prev, true
}
func (tr *bTree) delete(cn **node, max bool, key kind,
hint *bPathHint, depth int,
) (kind, bool) {
func (tr *BTreeG[T]) delete(cn **node[T], max bool, key T,
hint *PathHint, depth int,
) (T, bool) {
n := tr.cowLoad(cn)
var i int
var found bool
@@ -506,7 +496,7 @@ func (tr *bTree) delete(cn **node, max bool, key kind,
return tr.empty, false
}
var prev kind
var prev T
var deleted bool
if found {
if max {
@@ -529,13 +519,12 @@ func (tr *bTree) delete(cn **node, max bool, key kind,
tr.nodeRebalance(n, i)
}
return prev, true
}
// nodeRebalance rebalances the child nodes following a delete operation.
// Provide the index of the child node with the number of items that fell
// below minItems.
func (tr *bTree) nodeRebalance(n *node, i int) {
func (tr *BTreeG[T]) nodeRebalance(n *node[T], i int) {
if i == len(n.items) {
i--
}
@@ -618,7 +607,7 @@ func (tr *bTree) nodeRebalance(n *node, i int) {
// Ascend the tree within the range [pivot, last]
// Pass nil for pivot to scan all item in ascending order
// Return false to stop iterating
func (tr *bTree) Ascend(pivot kind, iter func(item kind) bool) {
func (tr *BTreeG[T]) Ascend(pivot T, iter func(item T) bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -630,8 +619,8 @@ func (tr *bTree) Ascend(pivot kind, iter func(item kind) bool) {
// The return value of this function determines whether we should keep iterating
// upon this functions return.
func (tr *bTree) ascend(n *node, pivot kind,
hint *bPathHint, depth int, iter func(item kind) bool,
func (tr *BTreeG[T]) ascend(n *node[T], pivot T,
hint *PathHint, depth int, iter func(item T) bool,
) bool {
i, found := tr.find(n, pivot, hint, depth)
if !found {
@@ -658,7 +647,7 @@ func (tr *bTree) ascend(n *node, pivot kind,
return true
}
func (tr *bTree) Reverse(iter func(item kind) bool) {
func (tr *BTreeG[T]) Reverse(iter func(item T) bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -668,7 +657,7 @@ func (tr *bTree) Reverse(iter func(item kind) bool) {
tr.root.reverse(iter)
}
func (n *node) reverse(iter func(item kind) bool) bool {
func (n *node[T]) reverse(iter func(item T) bool) bool {
if n.leaf() {
for i := len(n.items) - 1; i >= 0; i-- {
if !iter(n.items[i]) {
@@ -694,7 +683,7 @@ func (n *node) reverse(iter func(item kind) bool) bool {
// Descend the tree within the range [pivot, first]
// Pass nil for pivot to scan all item in descending order
// Return false to stop iterating
func (tr *bTree) Descend(pivot kind, iter func(item kind) bool) {
func (tr *BTreeG[T]) Descend(pivot T, iter func(item T) bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -704,8 +693,8 @@ func (tr *bTree) Descend(pivot kind, iter func(item kind) bool) {
tr.descend(tr.root, pivot, nil, 0, iter)
}
func (tr *bTree) descend(n *node, pivot kind,
hint *bPathHint, depth int, iter func(item kind) bool,
func (tr *BTreeG[T]) descend(n *node[T], pivot T,
hint *PathHint, depth int, iter func(item T) bool,
) bool {
i, found := tr.find(n, pivot, hint, depth)
if !found {
@@ -730,7 +719,7 @@ func (tr *bTree) descend(n *node, pivot kind,
}
// Load is for bulk loading pre-sorted items
func (tr *bTree) Load(item kind) (kind, bool) {
func (tr *BTreeG[T]) Load(item T) (T, bool) {
if tr.lock() {
defer tr.unlock()
}
@@ -765,8 +754,8 @@ func (tr *bTree) Load(item kind) (kind, bool) {
}
// Min returns the minimum item in tree.
// Returns nil if the tree has no items.
func (tr *bTree) Min() (kind, bool) {
// Returns nil if the treex has no items.
func (tr *BTreeG[T]) Min() (T, bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -784,7 +773,7 @@ func (tr *bTree) Min() (kind, bool) {
// Max returns the maximum item in tree.
// Returns nil if the tree has no items.
func (tr *bTree) Max() (kind, bool) {
func (tr *BTreeG[T]) Max() (T, bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -802,7 +791,7 @@ func (tr *bTree) Max() (kind, bool) {
// PopMin removes the minimum item in tree and returns it.
// Returns nil if the tree has no items.
func (tr *bTree) PopMin() (kind, bool) {
func (tr *BTreeG[T]) PopMin() (T, bool) {
if tr.lock() {
defer tr.unlock()
}
@@ -810,7 +799,7 @@ func (tr *bTree) PopMin() (kind, bool) {
return tr.empty, false
}
n := tr.cowLoad(&tr.root)
var item kind
var item T
for {
n.count-- // optimistically update counts
if n.leaf() {
@@ -841,9 +830,9 @@ func (tr *bTree) PopMin() (kind, bool) {
return tr.deleteHint(item, nil)
}
// PopMax removes the minimum item in tree and returns it.
// PopMax removes the maximum item in tree and returns it.
// Returns nil if the tree has no items.
func (tr *bTree) PopMax() (kind, bool) {
func (tr *BTreeG[T]) PopMax() (T, bool) {
if tr.lock() {
defer tr.unlock()
}
@@ -851,7 +840,7 @@ func (tr *bTree) PopMax() (kind, bool) {
return tr.empty, false
}
n := tr.cowLoad(&tr.root)
var item kind
var item T
for {
n.count-- // optimistically update counts
if n.leaf() {
@@ -883,7 +872,7 @@ func (tr *bTree) PopMax() (kind, bool) {
// GetAt returns the value at index.
// Return nil if the tree is empty or the index is out of bounds.
func (tr *bTree) GetAt(index int) (kind, bool) {
func (tr *BTreeG[T]) GetAt(index int) (T, bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -910,7 +899,7 @@ func (tr *bTree) GetAt(index int) (kind, bool) {
// DeleteAt deletes the item at index.
// Return nil if the tree is empty or the index is out of bounds.
func (tr *bTree) DeleteAt(index int) (kind, bool) {
func (tr *BTreeG[T]) DeleteAt(index int) (T, bool) {
if tr.lock() {
defer tr.unlock()
}
@@ -919,7 +908,7 @@ func (tr *bTree) DeleteAt(index int) (kind, bool) {
}
var pathbuf [8]uint8 // track the path
path := pathbuf[:0]
var item kind
var item T
n := tr.cowLoad(&tr.root)
outer:
for {
@@ -955,7 +944,7 @@ outer:
n = tr.cowLoad(&(*n.children)[i])
}
// revert the counts
var hint bPathHint
var hint PathHint
n = tr.root
for i := 0; i < len(path); i++ {
if i < len(hint.path) {
@@ -972,7 +961,7 @@ outer:
// Height returns the height of the tree.
// Returns zero if tree has no items.
func (tr *bTree) Height() int {
func (tr *BTreeG[T]) Height() int {
if tr.rlock() {
defer tr.runlock()
}
@@ -992,7 +981,7 @@ func (tr *bTree) Height() int {
// Walk iterates over all items in tree, in order.
// The items param will contain one or more items.
func (tr *bTree) Walk(iter func(item []kind) bool) {
func (tr *BTreeG[T]) Walk(iter func(item []T) bool) {
if tr.rlock() {
defer tr.runlock()
}
@@ -1001,7 +990,7 @@ func (tr *bTree) Walk(iter func(item []kind) bool) {
}
}
func (n *node) walk(iter func(item []kind) bool) bool {
func (n *node[T]) walk(iter func(item []T) bool) bool {
if n.leaf() {
if !iter(n.items) {
return false
@@ -1020,60 +1009,60 @@ func (n *node) walk(iter func(item []kind) bool) bool {
// Copy the tree. This is a copy-on-write operation and is very fast because
// it only performs a shadowed copy.
func (tr *bTree) Copy() *bTree {
func (tr *BTreeG[T]) Copy() *BTreeG[T] {
if tr.lock() {
defer tr.unlock()
}
tr.cow = new(cow)
tr2 := new(bTree)
tr.cow = atomic.AddUint64(&gcow, 1)
tr2 := new(BTreeG[T])
*tr2 = *tr
tr2.mu = new(sync.RWMutex)
tr2.cow = new(cow)
tr2.cow = atomic.AddUint64(&gcow, 1)
return tr2
}
func (tr *bTree) lock() bool {
func (tr *BTreeG[T]) lock() bool {
if tr.locks {
tr.mu.Lock()
}
return tr.locks
}
func (tr *bTree) unlock() {
func (tr *BTreeG[T]) unlock() {
tr.mu.Unlock()
}
func (tr *bTree) rlock() bool {
func (tr *BTreeG[T]) rlock() bool {
if tr.locks {
tr.mu.RLock()
}
return tr.locks
}
func (tr *bTree) runlock() {
func (tr *BTreeG[T]) runlock() {
tr.mu.RUnlock()
}
// Iter represents an iterator
type bIter struct {
tr *bTree
type GenericIter[T any] struct {
tr *BTreeG[T]
locked bool
seeked bool
atstart bool
atend bool
stack []iterStackItem
item kind
stack []genericIterStackItem[T]
item T
}
type iterStackItem struct {
n *node
type genericIterStackItem[T any] struct {
n *node[T]
i int
}
// Iter returns a read-only iterator.
// The Release method must be called finished with iterator.
func (tr *bTree) Iter() bIter {
var iter bIter
func (tr *BTreeG[T]) Iter() GenericIter[T] {
var iter GenericIter[T]
iter.tr = tr
iter.locked = tr.rlock()
return iter
@@ -1081,7 +1070,7 @@ func (tr *bTree) Iter() bIter {
// Seek to item greater-or-equal-to key.
// Returns false if there was no item found.
func (iter *bIter) Seek(key kind) bool {
func (iter *GenericIter[T]) Seek(key T) bool {
if iter.tr == nil {
return false
}
@@ -1093,16 +1082,14 @@ func (iter *bIter) Seek(key kind) bool {
n := iter.tr.root
for {
i, found := iter.tr.find(n, key, nil, 0)
iter.stack = append(iter.stack, iterStackItem{n, i})
iter.stack = append(iter.stack, genericIterStackItem[T]{n, i})
if found {
iter.item = n.items[i]
return true
}
if n.leaf() {
if i == len(n.items) {
iter.stack = iter.stack[:0]
return false
}
return true
iter.stack[len(iter.stack)-1].i--
return iter.Next()
}
n = (*n.children)[i]
}
@@ -1110,7 +1097,7 @@ func (iter *bIter) Seek(key kind) bool {
// First moves iterator to first item in tree.
// Returns false if the tree is empty.
func (iter *bIter) First() bool {
func (iter *GenericIter[T]) First() bool {
if iter.tr == nil {
return false
}
@@ -1123,7 +1110,7 @@ func (iter *bIter) First() bool {
}
n := iter.tr.root
for {
iter.stack = append(iter.stack, iterStackItem{n, 0})
iter.stack = append(iter.stack, genericIterStackItem[T]{n, 0})
if n.leaf() {
break
}
@@ -1136,7 +1123,7 @@ func (iter *bIter) First() bool {
// Last moves iterator to last item in tree.
// Returns false if the tree is empty.
func (iter *bIter) Last() bool {
func (iter *GenericIter[T]) Last() bool {
if iter.tr == nil {
return false
}
@@ -1147,7 +1134,7 @@ func (iter *bIter) Last() bool {
}
n := iter.tr.root
for {
iter.stack = append(iter.stack, iterStackItem{n, len(n.items)})
iter.stack = append(iter.stack, genericIterStackItem[T]{n, len(n.items)})
if n.leaf() {
iter.stack[len(iter.stack)-1].i--
break
@@ -1159,9 +1146,8 @@ func (iter *bIter) Last() bool {
return true
}
// First moves iterator to first item in tree.
// Returns false if the tree is empty.
func (iter *bIter) Release() {
// Release the iterator.
func (iter *GenericIter[T]) Release() {
if iter.tr == nil {
return
}
@@ -1176,7 +1162,7 @@ func (iter *bIter) Release() {
// Next moves iterator to the next item in iterator.
// Returns false if the tree is empty or the iterator is at the end of
// the tree.
func (iter *bIter) Next() bool {
func (iter *GenericIter[T]) Next() bool {
if iter.tr == nil {
return false
}
@@ -1208,7 +1194,7 @@ func (iter *bIter) Next() bool {
} else {
n := (*s.n.children)[s.i]
for {
iter.stack = append(iter.stack, iterStackItem{n, 0})
iter.stack = append(iter.stack, genericIterStackItem[T]{n, 0})
if n.leaf() {
break
}
@@ -1223,7 +1209,7 @@ func (iter *bIter) Next() bool {
// Prev moves iterator to the previous item in iterator.
// Returns false if the tree is empty or the iterator is at the beginning of
// the tree.
func (iter *bIter) Prev() bool {
func (iter *GenericIter[T]) Prev() bool {
if iter.tr == nil {
return false
}
@@ -1256,7 +1242,7 @@ func (iter *bIter) Prev() bool {
} else {
n := (*s.n.children)[s.i]
for {
iter.stack = append(iter.stack, iterStackItem{n, len(n.items)})
iter.stack = append(iter.stack, genericIterStackItem[T]{n, len(n.items)})
if n.leaf() {
iter.stack[len(iter.stack)-1].i--
break
@@ -1270,6 +1256,48 @@ func (iter *bIter) Prev() bool {
}
// Item returns the current iterator item.
func (iter *bIter) Item() kind {
func (iter *GenericIter[T]) Item() T {
return iter.item
}
// Items returns all the items in order.
func (tr *BTreeG[T]) Items() []T {
items := make([]T, 0, tr.Len())
if tr.root != nil {
items = tr.root.aitems(items)
}
return items
}
func (n *node[T]) aitems(items []T) []T {
if n.leaf() {
return append(items, n.items...)
}
for i := 0; i < len(n.items); i++ {
items = (*n.children)[i].aitems(items)
items = append(items, n.items[i])
}
return (*n.children)[len(*n.children)-1].aitems(items)
}
// Generic BTree
// Deprecated: use BTreeG
type Generic[T any] struct {
*BTreeG[T]
}
// NewGeneric returns a generic BTree
// Deprecated: use NewBTreeG
func NewGeneric[T any](less func(a, b T) bool) *Generic[T] {
return &Generic[T]{NewBTreeGOptions(less, Options{})}
}
// NewGenericOptions returns a generic BTree
// Deprecated: use NewBTreeGOptions
func NewGenericOptions[T any](less func(a, b T) bool, opts Options) *Generic[T] {
return &Generic[T]{NewBTreeGOptions(less, opts)}
}
func (tr *Generic[T]) Copy() *Generic[T] {
return &Generic[T]{tr.BTreeG.Copy()}
}

1056
vendor/github.com/tidwall/btree/map.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

168
vendor/github.com/tidwall/btree/set.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
package btree
type Set[K ordered] struct {
base Map[K, struct{}]
}
// Copy
func (tr *Set[K]) Copy() *Set[K] {
tr2 := new(Set[K])
tr2.base = *tr.base.Copy()
return tr2
}
// Insert an item
func (tr *Set[K]) Insert(key K) {
tr.base.Set(key, struct{}{})
}
func (tr *Set[K]) Scan(iter func(key K) bool) {
tr.base.Scan(func(key K, value struct{}) bool {
return iter(key)
})
}
// Get a value for key
func (tr *Set[K]) Contains(key K) bool {
_, ok := tr.base.Get(key)
return ok
}
// Len returns the number of items in the tree
func (tr *Set[K]) Len() int {
return tr.base.Len()
}
// Delete an item
func (tr *Set[K]) Delete(key K) {
tr.base.Delete(key)
}
// Ascend the tree within the range [pivot, last]
// Pass nil for pivot to scan all item in ascending order
// Return false to stop iterating
func (tr *Set[K]) Ascend(pivot K, iter func(key K) bool) {
tr.base.Ascend(pivot, func(key K, value struct{}) bool {
return iter(key)
})
}
func (tr *Set[K]) Reverse(iter func(key K) bool) {
tr.base.Reverse(func(key K, value struct{}) bool {
return iter(key)
})
}
// Descend the tree within the range [pivot, first]
// Pass nil for pivot to scan all item in descending order
// Return false to stop iterating
func (tr *Set[K]) Descend(pivot K, iter func(key K) bool) {
tr.base.Descend(pivot, func(key K, value struct{}) bool {
return iter(key)
})
}
// Load is for bulk loading pre-sorted items
func (tr *Set[K]) Load(key K) {
tr.base.Load(key, struct{}{})
}
// Min returns the minimum item in tree.
// Returns nil if the treex has no items.
func (tr *Set[K]) Min() (K, bool) {
key, _, ok := tr.base.Min()
return key, ok
}
// Max returns the maximum item in tree.
// Returns nil if the tree has no items.
func (tr *Set[K]) Max() (K, bool) {
key, _, ok := tr.base.Max()
return key, ok
}
// PopMin removes the minimum item in tree and returns it.
// Returns nil if the tree has no items.
func (tr *Set[K]) PopMin() (K, bool) {
key, _, ok := tr.base.PopMin()
return key, ok
}
// PopMax removes the maximum item in tree and returns it.
// Returns nil if the tree has no items.
func (tr *Set[K]) PopMax() (K, bool) {
key, _, ok := tr.base.PopMax()
return key, ok
}
// GetAt returns the value at index.
// Return nil if the tree is empty or the index is out of bounds.
func (tr *Set[K]) GetAt(index int) (K, bool) {
key, _, ok := tr.base.GetAt(index)
return key, ok
}
// DeleteAt deletes the item at index.
// Return nil if the tree is empty or the index is out of bounds.
func (tr *Set[K]) DeleteAt(index int) (K, bool) {
key, _, ok := tr.base.DeleteAt(index)
return key, ok
}
// Height returns the height of the tree.
// Returns zero if tree has no items.
func (tr *Set[K]) Height() int {
return tr.base.Height()
}
// SetIter represents an iterator for btree.Set
type SetIter[K ordered] struct {
base MapIter[K, struct{}]
}
// Iter returns a read-only iterator.
func (tr *Set[K]) Iter() SetIter[K] {
return SetIter[K]{tr.base.Iter()}
}
// Seek to item greater-or-equal-to key.
// Returns false if there was no item found.
func (iter *SetIter[K]) Seek(key K) bool {
return iter.base.Seek(key)
}
// First moves iterator to first item in tree.
// Returns false if the tree is empty.
func (iter *SetIter[K]) First() bool {
return iter.base.First()
}
// Last moves iterator to last item in tree.
// Returns false if the tree is empty.
func (iter *SetIter[K]) Last() bool {
return iter.base.Last()
}
// Next moves iterator to the next item in iterator.
// Returns false if the tree is empty or the iterator is at the end of
// the tree.
func (iter *SetIter[K]) Next() bool {
return iter.base.Next()
}
// Prev moves iterator to the previous item in iterator.
// Returns false if the tree is empty or the iterator is at the beginning of
// the tree.
func (iter *SetIter[K]) Prev() bool {
return iter.base.Prev()
}
// Key returns the current iterator item key.
func (iter *SetIter[K]) Key() K {
return iter.base.Key()
}
// Keys returns all the keys in order.
func (tr *Set[K]) Keys() []K {
return tr.base.Keys()
}