mirror of
				https://github.com/ergochat/ergo.git
				synced 2025-10-31 05:47:22 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			452 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # btree
 | |
| 
 | |
| [](https://godoc.org/github.com/tidwall/btree)
 | |
| 
 | |
| An efficient [B-tree](https://en.wikipedia.org/wiki/B-tree) implementation in Go.
 | |
| 
 | |
| ## Features
 | |
| 
 | |
| - 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.
 | |
| - `Copy()` method with copy-on-write support.
 | |
| - Thread-safe operations.
 | |
| - [Path hinting](PATH_HINT.md) optimization for operations with nearby keys.
 | |
| 
 | |
| ## Using
 | |
| 
 | |
| To start using this package, install Go and run:
 | |
| 
 | |
| ```sh
 | |
| $ go get github.com/tidwall/btree
 | |
| ```
 | |
| 
 | |
| ## 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
 | |
| 
 | |
| 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 interface{}) bool {
 | |
| 	i1, i2 := a.(*Item), b.(*Item)
 | |
| 	return 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 {
 | |
| 	i1, i2 := a.(*Item), b.(*Item)
 | |
| 	if 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.
 | |
| 	keys := btree.New(byKeys)
 | |
| 	vals := btree.New(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.Ascend(nil, func(item interface{}) bool {
 | |
| 		kvi := item.(*Item)
 | |
| 		fmt.Printf("%s %s\n", kvi.Key, kvi.Val)
 | |
| 		return true
 | |
| 	})
 | |
| 
 | |
| 	fmt.Printf("\n")
 | |
| 	// Iterate over each user in the val tree
 | |
| 	vals.Ascend(nil, func(item interface{}) bool {
 | |
| 		kvi := item.(*Item)
 | |
| 		fmt.Printf("%s %s\n", kvi.Key, kvi.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
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Performance
 | |
| 
 | |
| See [tidwall/btree-benchmark](https://github.com/tidwall/btree-benchmark) for benchmark numbers.
 | |
| 
 | |
| ## Contact
 | |
| 
 | |
| Josh Baker [@tidwall](http://twitter.com/tidwall)
 | |
| 
 | |
| ## License
 | |
| 
 | |
| Source code is available under the MIT [License](/LICENSE).
 | 
