mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-15 08:29:31 +01:00
9.9 KiB
9.9 KiB
btree
An efficient B-tree implementation in Go.
Features
- Support for Generics (Go 1.18+).
Map
andSet
types for ordered key-value maps and sets,- Fast bulk loading for pre-ordered data using the
Load()
method. Copy()
method with copy-on-write support.- Thread-safe operations.
- Path hinting optimization for operations with nearby keys.
Using
To start using this package, install Go and run:
$ go get github.com/tidwall/btree
B-tree types
This package includes the following types of B-trees:
btree.Map
: A fast B-tree for storing ordered key value pairs. Go 1.18+btree.Set
: LikeMap
, but only for storing keys. Go 1.18+btree.BTreeG
: A feature-rich B-tree for storing data using a custom comparator. Go 1.18+btree.BTree
: LikeBTreeG
but uses theinterface{}
type for data. Backwards compatible. Go 1.16+
btree.Map
// Basic
(key, value) // insert or replace an item
Set(key, value) // get an existing item
Get(key) // delete an item
Delete() // return the number of items in the map
Len
// Iteration
(iter) // scan items in ascending order
Scan(iter) // scan items in descending order
Reverse(key, iter) // scan items in ascending order that are >= to key
Ascend(key, iter) // scan items in descending order that are <= to key.
Descend() // returns a read-only iterator for for-loops.
Iter
// Array-like operations
(index) // returns the item at index
GetAt(index) // deletes the item at index
DeleteAt
// Bulk-loading
(key, value) // load presorted items into tree Load
Example
package main
import (
"fmt"
"github.com/tidwall/btree"
)
func main() {
// create a map
var users btree.Map[string, string]
// add some 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")
users
// Iterate over the maps and print each user
.Scan(func(key, value string) bool {
users.Printf("%s %s\n", key, value)
fmtreturn true
})
.Printf("\n")
fmt
// Delete a couple
.Delete("user:5")
users.Delete("user:1")
users
// print the map again
.Scan(func(key, value string) bool {
users.Printf("%s %s\n", key, value)
fmtreturn true
})
.Printf("\n")
fmt
// 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
// Basic
(key) // insert an item
Insert(key) // test if item exists
Contains(key) // delete an item
Delete() // return the number of items in the set
Len
// Iteration
(iter) // scan items in ascending order
Scan(iter) // scan items in descending order
Reverse(key, iter) // scan items in ascending order that are >= to key
Ascend(key, iter) // scan items in descending order that are <= to key.
Descend() // returns a read-only iterator for for-loops.
Iter
// Array-like operations
(index) // returns the item at index
GetAt(index) // deletes the item at index
DeleteAt
// Bulk-loading
(key) // load presorted item into tree Load
Example
package main
import (
"fmt"
"github.com/tidwall/btree"
)
func main() {
// create a set
var names btree.Set[string]
// add some names
.Insert("Jane")
names.Insert("Andrea")
names.Insert("Steve")
names.Insert("Andy")
names.Insert("Janet")
names.Insert("Andy")
names
// Iterate over the maps and print each user
.Scan(func(key string) bool {
names.Printf("%s\n", key)
fmtreturn true
})
.Printf("\n")
fmt
// Delete a couple
.Delete("Steve")
names.Delete("Andy")
names
// print the map again
.Scan(func(key string) bool {
names.Printf("%s\n", key)
fmtreturn true
})
.Printf("\n")
fmt
// Output:
// Andrea
// Andy
// Jane
// Janet
// Steve
//
// Andrea
// Jane
// Janet
}
btree.BTreeG
// Basic
(item) // insert or replace an item
Set(item) // get an existing item
Get(item) // delete an item
Delete() // return the number of items in the btree
Len
// Iteration
(iter) // scan items in ascending order
Scan(iter) // scan items in descending order
Reverse(key, iter) // scan items in ascending order that are >= to key
Ascend(key, iter) // scan items in descending order that are <= to key.
Descend() // returns a read-only iterator for for-loops.
Iter
// Array-like operations
(index) // returns the item at index
GetAt(index) // deletes the item at index
DeleteAt
// Bulk-loading
(item) // load presorted items into tree
Load
// Path hinting
(item, *hint) // insert or replace an existing item
SetHint(item, *hint) // get an existing item
GetHint(item, *hint) // delete an item
DeleteHint
// Copy-on-write
() // copy the btree Copy
Example
package main
import (
"fmt"
"github.com/tidwall/btree"
)
type Item struct {
, Val string
Key}
// 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.
:= btree.NewBTreeG[Item](byKeys)
keys := btree.NewBTreeG[Item](byVals)
vals
// Create some items.
:= []Item{
users {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"},
Item}
// Insert each user into both trees
for _, user := range users {
.Set(user)
keys.Set(user)
vals}
// Iterate over each user in the key tree
.Scan(func(item Item) bool {
keys.Printf("%s %s\n", item.Key, item.Val)
fmtreturn true
})
.Printf("\n")
fmt
// Iterate over each user in the val tree
.Scan(func(item Item) bool {
vals.Printf("%s %s\n", item.Key, item.Val)
fmtreturn 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
// Basic
(item) // insert or replace an item
Set(item) // get an existing item
Get(item) // delete an item
Delete() // return the number of items in the btree
Len
// Iteration
(iter) // scan items in ascending order
Scan(iter) // scan items in descending order
Reverse(key, iter) // scan items in ascending order that are >= to key
Ascend(key, iter) // scan items in descending order that are <= to key.
Descend() // returns a read-only iterator for for-loops.
Iter
// Array-like operations
(index) // returns the item at index
GetAt(index) // deletes the item at index
DeleteAt
// Bulk-loading
(item) // load presorted items into tree
Load
// Path hinting
(item, *hint) // insert or replace an existing item
SetHint(item, *hint) // get an existing item
GetHint(item, *hint) // delete an item
DeleteHint
// Copy-on-write
() // copy the btree Copy
Example
package main
import (
"fmt"
"github.com/tidwall/btree"
)
type Item struct {
, Val string
Key}
// byKeys is a comparison function that compares item keys and returns true
// when a is less than b.
func byKeys(a, b interface{}) bool {
, i2 := a.(*Item), b.(*Item)
i1return i1.Key < i2.Key
}
// byVals is a comparison function that compares item values and returns true
// when a is less than b.
func byVals(a, b interface{}) bool {
, i2 := a.(*Item), b.(*Item)
i1if i1.Val < i2.Val {
return true
}
if i1.Val > i2.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.
:= btree.New(byKeys)
keys := btree.New(byVals)
vals
// Create some items.
:= []*Item{
users &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 {
.Set(user)
keys.Set(user)
vals}
// Iterate over each user in the key tree
.Ascend(nil, func(item interface{}) bool {
keys:= item.(*Item)
kvi .Printf("%s %s\n", kvi.Key, kvi.Val)
fmtreturn true
})
.Printf("\n")
fmt// Iterate over each user in the val tree
.Ascend(nil, func(item interface{}) bool {
vals:= item.(*Item)
kvi .Printf("%s %s\n", kvi.Key, kvi.Val)
fmtreturn 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
}
Performance
See tidwall/btree-benchmark for benchmark numbers.
Contact
Josh Baker @tidwall
License
Source code is available under the MIT License.