mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-15 00:19:29 +01:00
upgrade buntdb and dependencies
This commit is contained in:
parent
b2087977d0
commit
3f5de80afd
6
go.mod
6
go.mod
@ -17,7 +17,7 @@ require (
|
||||
github.com/onsi/ginkgo v1.12.0 // indirect
|
||||
github.com/onsi/gomega v1.9.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
github.com/tidwall/buntdb v1.2.9
|
||||
github.com/tidwall/buntdb v1.2.10
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208
|
||||
github.com/xdg-go/scram v1.0.2
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
||||
@ -28,8 +28,8 @@ require (
|
||||
require github.com/gofrs/flock v0.8.1
|
||||
|
||||
require (
|
||||
github.com/tidwall/btree v1.1.0 // indirect
|
||||
github.com/tidwall/gjson v1.12.1 // indirect
|
||||
github.com/tidwall/btree v1.4.2 // indirect
|
||||
github.com/tidwall/gjson v1.14.3 // indirect
|
||||
github.com/tidwall/grect v0.1.4 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -49,14 +49,20 @@ github.com/tidwall/btree v0.6.1 h1:75VVgBeviiDO+3g4U+7+BaNBNhNINxB0ULPT3fs9pMY=
|
||||
github.com/tidwall/btree v0.6.1/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
|
||||
github.com/tidwall/btree v1.1.0 h1:5P+9WU8ui5uhmcg3SoPyTwoI0mVyZ1nps7YQzTZFkYM=
|
||||
github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
|
||||
github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g=
|
||||
github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
|
||||
github.com/tidwall/buntdb v1.2.7 h1:SIyObKAymzLyGhDeIhVk2Yc1/EwfCC75Uyu77CHlVoA=
|
||||
github.com/tidwall/buntdb v1.2.7/go.mod h1:b6KvZM27x/8JLI5hgRhRu60pa3q0Tz9c50TyD46OHUM=
|
||||
github.com/tidwall/buntdb v1.2.9 h1:XVz684P7X6HCTrdr385yDZWB1zt/n20ZNG3M1iGyFm4=
|
||||
github.com/tidwall/buntdb v1.2.9/go.mod h1:IwyGSvvDg6hnKSIhtdZ0AqhCZGH8ukdtCAzaP8fI1X4=
|
||||
github.com/tidwall/buntdb v1.2.10 h1:U/ebfkmYPBnyiNZIirUiWFcxA/mgzjbKlyPynFsPtyM=
|
||||
github.com/tidwall/buntdb v1.2.10/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
|
||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/grect v0.1.3 h1:z9YwQAMUxVSBde3b7Sl8Da37rffgNfZ6Fq6h9t6KdXE=
|
||||
github.com/tidwall/grect v0.1.3/go.mod h1:8GMjwh3gPZVpLBI/jDz9uslCe0dpxRpWDdtN0lWAS/E=
|
||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
||||
|
428
vendor/github.com/tidwall/btree/README.md
generated
vendored
428
vendor/github.com/tidwall/btree/README.md
generated
vendored
@ -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
|
||||
|
||||
|
78
vendor/github.com/tidwall/btree/btree.go
generated
vendored
78
vendor/github.com/tidwall/btree/btree.go
generated
vendored
@ -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,13 +22,13 @@ 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,
|
||||
base: NewBTreeGOptions(less,
|
||||
Options{
|
||||
NoLocks: true,
|
||||
}),
|
||||
}
|
||||
@ -44,17 +36,19 @@ func NewNonConcurrent(less func(a, b interface{}) bool) *BTree {
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
@ -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,
|
||||
) (index int, found bool) {
|
||||
if hint == nil {
|
||||
// fast path for no hinting
|
||||
low := 0
|
||||
high := len(n.items)
|
||||
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 {
|
||||
mid := (low + high) / 2
|
||||
if !tr.Less(key, n.items[mid]) {
|
||||
low = mid + 1
|
||||
h := int(uint(low+high) >> 1)
|
||||
if !tr.less(key, n.items[h]) {
|
||||
low = h + 1
|
||||
} else {
|
||||
high = mid
|
||||
high = h
|
||||
}
|
||||
}
|
||||
if low > 0 && !tr.Less(n.items[low-1], key) {
|
||||
if low > 0 && !tr.less(n.items[low-1], key) {
|
||||
return low - 1, true
|
||||
}
|
||||
return low, false
|
||||
}
|
||||
}
|
||||
|
||||
// Try using hint.
|
||||
func (tr *BTreeG[T]) find(n *node[T], key T, hint *PathHint, depth int,
|
||||
) (index int, found bool) {
|
||||
if hint == nil {
|
||||
return tr.bsearch(n, key)
|
||||
}
|
||||
return tr.hintsearch(n, key, hint, depth)
|
||||
}
|
||||
|
||||
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)
|
||||
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, len((*n.children)[i+1:]), maxItems+1)
|
||||
*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) {
|
||||
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
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
168
vendor/github.com/tidwall/btree/set.go
generated
vendored
Normal 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()
|
||||
}
|
15
vendor/github.com/tidwall/buntdb/buntdb.go
generated
vendored
15
vendor/github.com/tidwall/buntdb/buntdb.go
generated
vendored
@ -7,6 +7,7 @@ package buntdb
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
@ -749,13 +750,13 @@ func (db *DB) Shrink() error {
|
||||
if err := db.file.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Any failures below here is really bad. So just panic.
|
||||
// Any failures below here are really bad. So just panic.
|
||||
if err := os.Rename(tmpname, fname); err != nil {
|
||||
panic(err)
|
||||
panicErr(err)
|
||||
}
|
||||
db.file, err = os.OpenFile(fname, os.O_CREATE|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panicErr(err)
|
||||
}
|
||||
pos, err := db.file.Seek(0, 2)
|
||||
if err != nil {
|
||||
@ -766,6 +767,10 @@ func (db *DB) Shrink() error {
|
||||
}()
|
||||
}
|
||||
|
||||
func panicErr(err error) error {
|
||||
panic(fmt.Errorf("buntdb: %w", err))
|
||||
}
|
||||
|
||||
// readLoad reads from the reader and loads commands into the database.
|
||||
// modTime is the modified time of the reader, should be no greater than
|
||||
// the current time.Now().
|
||||
@ -1209,10 +1214,10 @@ func (tx *Tx) Commit() error {
|
||||
// should be killed to avoid corrupting the file.
|
||||
pos, err := tx.db.file.Seek(-int64(n), 1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panicErr(err)
|
||||
}
|
||||
if err := tx.db.file.Truncate(pos); err != nil {
|
||||
panic(err)
|
||||
panicErr(err)
|
||||
}
|
||||
}
|
||||
tx.rollbackInner()
|
||||
|
7
vendor/github.com/tidwall/gjson/README.md
generated
vendored
7
vendor/github.com/tidwall/gjson/README.md
generated
vendored
@ -16,6 +16,10 @@ It has features such as [one line retrieval](#get-a-value), [dot notation paths]
|
||||
|
||||
Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool.
|
||||
|
||||
This README is a quick overview of how to use GJSON, for more information check out [GJSON Syntax](SYNTAX.md).
|
||||
|
||||
GJSON is also available for [Python](https://github.com/volans-/gjson-py) and [Rust](https://github.com/tidwall/gjson.rs)
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
@ -204,6 +208,9 @@ There are currently the following built-in modifiers:
|
||||
- `@join`: Joins multiple objects into a single object.
|
||||
- `@keys`: Returns an array of keys for an object.
|
||||
- `@values`: Returns an array of values for an object.
|
||||
- `@tostr`: Converts json to a string. Wraps a json string.
|
||||
- `@fromstr`: Converts a string from json. Unwraps a json string.
|
||||
- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
|
||||
|
||||
### Modifier arguments
|
||||
|
||||
|
7
vendor/github.com/tidwall/gjson/SYNTAX.md
generated
vendored
7
vendor/github.com/tidwall/gjson/SYNTAX.md
generated
vendored
@ -22,7 +22,7 @@ Use the [GJSON Playground](https://gjson.dev) to experiment with the syntax onli
|
||||
|
||||
A GJSON Path is intended to be easily expressed as a series of components seperated by a `.` character.
|
||||
|
||||
Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, and `?`.
|
||||
Along with `.` character, there are a few more that have special meaning, including `|`, `#`, `@`, `\`, `*`, `!`, and `?`.
|
||||
|
||||
## Example
|
||||
|
||||
@ -77,7 +77,7 @@ Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`.
|
||||
fav\.movie "Deer Hunter"
|
||||
```
|
||||
|
||||
You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in you source code.
|
||||
You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in your source code.
|
||||
|
||||
```go
|
||||
// Go
|
||||
@ -238,6 +238,9 @@ There are currently the following built-in modifiers:
|
||||
- `@join`: Joins multiple objects into a single object.
|
||||
- `@keys`: Returns an array of keys for an object.
|
||||
- `@values`: Returns an array of values for an object.
|
||||
- `@tostr`: Converts json to a string. Wraps a json string.
|
||||
- `@fromstr`: Converts a string from json. Unwraps a json string.
|
||||
- `@group`: Groups arrays of objects. See [e4fc67c](https://github.com/tidwall/gjson/commit/e4fc67c92aeebf2089fabc7872f010e340d105db).
|
||||
|
||||
#### Modifier arguments
|
||||
|
||||
|
183
vendor/github.com/tidwall/gjson/gjson.go
generated
vendored
183
vendor/github.com/tidwall/gjson/gjson.go
generated
vendored
@ -2,7 +2,6 @@
|
||||
package gjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -214,6 +213,11 @@ func (t Result) IsArray() bool {
|
||||
return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['
|
||||
}
|
||||
|
||||
// IsBool returns true if the result value is a JSON boolean.
|
||||
func (t Result) IsBool() bool {
|
||||
return t.Type == True || t.Type == False
|
||||
}
|
||||
|
||||
// ForEach iterates through values.
|
||||
// If the result represents a non-existent value, then no values will be
|
||||
// iterated. If the result is an Object, the iterator will pass the key and
|
||||
@ -771,7 +775,7 @@ func parseArrayPath(path string) (r arrayPathResult) {
|
||||
}
|
||||
if path[i] == '.' {
|
||||
r.part = path[:i]
|
||||
if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1]) {
|
||||
if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1:]) {
|
||||
r.pipe = path[i+1:]
|
||||
r.piped = true
|
||||
} else {
|
||||
@ -932,8 +936,23 @@ right:
|
||||
}
|
||||
|
||||
// peek at the next byte and see if it's a '@', '[', or '{'.
|
||||
func isDotPiperChar(c byte) bool {
|
||||
return !DisableModifiers && (c == '@' || c == '[' || c == '{')
|
||||
func isDotPiperChar(s string) bool {
|
||||
if DisableModifiers {
|
||||
return false
|
||||
}
|
||||
c := s[0]
|
||||
if c == '@' {
|
||||
// check that the next component is *not* a modifier.
|
||||
i := 1
|
||||
for ; i < len(s); i++ {
|
||||
if s[i] == '.' || s[i] == '|' || s[i] == ':' {
|
||||
break
|
||||
}
|
||||
}
|
||||
_, ok := modifiers[s[1:i]]
|
||||
return ok
|
||||
}
|
||||
return c == '[' || c == '{'
|
||||
}
|
||||
|
||||
type objectPathResult struct {
|
||||
@ -955,7 +974,7 @@ func parseObjectPath(path string) (r objectPathResult) {
|
||||
}
|
||||
if path[i] == '.' {
|
||||
r.part = path[:i]
|
||||
if i < len(path)-1 && isDotPiperChar(path[i+1]) {
|
||||
if i < len(path)-1 && isDotPiperChar(path[i+1:]) {
|
||||
r.pipe = path[i+1:]
|
||||
r.piped = true
|
||||
} else {
|
||||
@ -985,7 +1004,7 @@ func parseObjectPath(path string) (r objectPathResult) {
|
||||
continue
|
||||
} else if path[i] == '.' {
|
||||
r.part = string(epart)
|
||||
if i < len(path)-1 && isDotPiperChar(path[i+1]) {
|
||||
if i < len(path)-1 && isDotPiperChar(path[i+1:]) {
|
||||
r.pipe = path[i+1:]
|
||||
r.piped = true
|
||||
} else {
|
||||
@ -1819,17 +1838,64 @@ func isSimpleName(component string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func appendJSONString(dst []byte, s string) []byte {
|
||||
var hexchars = [...]byte{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'a', 'b', 'c', 'd', 'e', 'f',
|
||||
}
|
||||
|
||||
func appendHex16(dst []byte, x uint16) []byte {
|
||||
return append(dst,
|
||||
hexchars[x>>12&0xF], hexchars[x>>8&0xF],
|
||||
hexchars[x>>4&0xF], hexchars[x>>0&0xF],
|
||||
)
|
||||
}
|
||||
|
||||
// AppendJSONString is a convenience function that converts the provided string
|
||||
// to a valid JSON string and appends it to dst.
|
||||
func AppendJSONString(dst []byte, s string) []byte {
|
||||
dst = append(dst, make([]byte, len(s)+2)...)
|
||||
dst = append(dst[:len(dst)-len(s)-2], '"')
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
|
||||
d, _ := json.Marshal(s)
|
||||
return append(dst, string(d)...)
|
||||
if s[i] < ' ' {
|
||||
dst = append(dst, '\\')
|
||||
switch s[i] {
|
||||
case '\n':
|
||||
dst = append(dst, 'n')
|
||||
case '\r':
|
||||
dst = append(dst, 'r')
|
||||
case '\t':
|
||||
dst = append(dst, 't')
|
||||
default:
|
||||
dst = append(dst, 'u')
|
||||
dst = appendHex16(dst, uint16(s[i]))
|
||||
}
|
||||
} else if s[i] == '>' || s[i] == '<' || s[i] == '&' {
|
||||
dst = append(dst, '\\', 'u')
|
||||
dst = appendHex16(dst, uint16(s[i]))
|
||||
} else if s[i] == '\\' {
|
||||
dst = append(dst, '\\', '\\')
|
||||
} else if s[i] == '"' {
|
||||
dst = append(dst, '\\', '"')
|
||||
} else if s[i] > 127 {
|
||||
// read utf8 character
|
||||
r, n := utf8.DecodeRuneInString(s[i:])
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
if r == utf8.RuneError && n == 1 {
|
||||
dst = append(dst, `\ufffd`...)
|
||||
} else if r == '\u2028' || r == '\u2029' {
|
||||
dst = append(dst, `\u202`...)
|
||||
dst = append(dst, hexchars[r&0xF])
|
||||
} else {
|
||||
dst = append(dst, s[i:i+n]...)
|
||||
}
|
||||
i = i + n - 1
|
||||
} else {
|
||||
dst = append(dst, s[i])
|
||||
}
|
||||
}
|
||||
dst = append(dst, '"')
|
||||
dst = append(dst, s...)
|
||||
dst = append(dst, '"')
|
||||
return dst
|
||||
return append(dst, '"')
|
||||
}
|
||||
|
||||
type parseContext struct {
|
||||
@ -1919,14 +1985,14 @@ func Get(json, path string) Result {
|
||||
if sub.name[0] == '"' && Valid(sub.name) {
|
||||
b = append(b, sub.name...)
|
||||
} else {
|
||||
b = appendJSONString(b, sub.name)
|
||||
b = AppendJSONString(b, sub.name)
|
||||
}
|
||||
} else {
|
||||
last := nameOfLast(sub.path)
|
||||
if isSimpleName(last) {
|
||||
b = appendJSONString(b, last)
|
||||
b = AppendJSONString(b, last)
|
||||
} else {
|
||||
b = appendJSONString(b, "_")
|
||||
b = AppendJSONString(b, "_")
|
||||
}
|
||||
}
|
||||
b = append(b, ':')
|
||||
@ -2669,6 +2735,9 @@ var modifiers = map[string]func(json, arg string) string{
|
||||
"valid": modValid,
|
||||
"keys": modKeys,
|
||||
"values": modValues,
|
||||
"tostr": modToStr,
|
||||
"fromstr": modFromStr,
|
||||
"group": modGroup,
|
||||
}
|
||||
|
||||
// AddModifier binds a custom modifier command to the GJSON syntax.
|
||||
@ -2954,6 +3023,56 @@ func modValid(json, arg string) string {
|
||||
return json
|
||||
}
|
||||
|
||||
// @fromstr converts a string to json
|
||||
// "{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"}
|
||||
func modFromStr(json, arg string) string {
|
||||
if !Valid(json) {
|
||||
return ""
|
||||
}
|
||||
return Parse(json).String()
|
||||
}
|
||||
|
||||
// @tostr converts a string to json
|
||||
// {"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}"
|
||||
func modToStr(str, arg string) string {
|
||||
return string(AppendJSONString(nil, str))
|
||||
}
|
||||
|
||||
func modGroup(json, arg string) string {
|
||||
res := Parse(json)
|
||||
if !res.IsObject() {
|
||||
return ""
|
||||
}
|
||||
var all [][]byte
|
||||
res.ForEach(func(key, value Result) bool {
|
||||
if !value.IsArray() {
|
||||
return true
|
||||
}
|
||||
var idx int
|
||||
value.ForEach(func(_, value Result) bool {
|
||||
if idx == len(all) {
|
||||
all = append(all, []byte{})
|
||||
}
|
||||
all[idx] = append(all[idx], ("," + key.Raw + ":" + value.Raw)...)
|
||||
idx++
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
var data []byte
|
||||
data = append(data, '[')
|
||||
for i, item := range all {
|
||||
if i > 0 {
|
||||
data = append(data, ',')
|
||||
}
|
||||
data = append(data, '{')
|
||||
data = append(data, item[1:]...)
|
||||
data = append(data, '}')
|
||||
}
|
||||
data = append(data, ']')
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// stringHeader instead of reflect.StringHeader
|
||||
type stringHeader struct {
|
||||
data unsafe.Pointer
|
||||
@ -3088,6 +3207,20 @@ func revSquash(json string) string {
|
||||
return json
|
||||
}
|
||||
|
||||
// Paths returns the original GJSON paths for a Result where the Result came
|
||||
// from a simple query path that returns an array, like:
|
||||
//
|
||||
// gjson.Get(json, "friends.#.first")
|
||||
//
|
||||
// The returned value will be in the form of a JSON array:
|
||||
//
|
||||
// ["friends.0.first","friends.1.first","friends.2.first"]
|
||||
//
|
||||
// The param 'json' must be the original JSON used when calling Get.
|
||||
//
|
||||
// Returns an empty string if the paths cannot be determined, which can happen
|
||||
// when the Result came from a path that contained a multipath, modifier,
|
||||
// or a nested query.
|
||||
func (t Result) Paths(json string) []string {
|
||||
if t.Indexes == nil {
|
||||
return nil
|
||||
@ -3103,8 +3236,20 @@ func (t Result) Paths(json string) []string {
|
||||
return paths
|
||||
}
|
||||
|
||||
// Path returns the original GJSON path for Result.
|
||||
// The json param must be the original JSON used when calling Get.
|
||||
// Path returns the original GJSON path for a Result where the Result came
|
||||
// from a simple path that returns a single value, like:
|
||||
//
|
||||
// gjson.Get(json, "friends.#(last=Murphy)")
|
||||
//
|
||||
// The returned value will be in the form of a JSON string:
|
||||
//
|
||||
// "friends.0"
|
||||
//
|
||||
// The param 'json' must be the original JSON used when calling Get.
|
||||
//
|
||||
// Returns an empty string if the paths cannot be determined, which can happen
|
||||
// when the Result came from a path that contained a multipath, modifier,
|
||||
// or a nested query.
|
||||
func (t Result) Path(json string) string {
|
||||
var path []byte
|
||||
var comps []string // raw components
|
||||
|
11
vendor/modules.txt
vendored
11
vendor/modules.txt
vendored
@ -45,14 +45,13 @@ github.com/okzk/sdnotify
|
||||
## explicit
|
||||
# github.com/stretchr/testify v1.4.0
|
||||
## explicit
|
||||
# github.com/tidwall/btree v1.1.0
|
||||
## explicit; go 1.16
|
||||
# github.com/tidwall/btree v1.4.2
|
||||
## explicit; go 1.18
|
||||
github.com/tidwall/btree
|
||||
github.com/tidwall/btree/internal
|
||||
# github.com/tidwall/buntdb v1.2.9
|
||||
## explicit; go 1.16
|
||||
# github.com/tidwall/buntdb v1.2.10
|
||||
## explicit; go 1.18
|
||||
github.com/tidwall/buntdb
|
||||
# github.com/tidwall/gjson v1.12.1
|
||||
# github.com/tidwall/gjson v1.14.3
|
||||
## explicit; go 1.12
|
||||
github.com/tidwall/gjson
|
||||
# github.com/tidwall/grect v0.1.4
|
||||
|
Loading…
Reference in New Issue
Block a user