mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-15 08:29:31 +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/ginkgo v1.12.0 // indirect
|
||||||
github.com/onsi/gomega v1.9.0 // indirect
|
github.com/onsi/gomega v1.9.0 // indirect
|
||||||
github.com/stretchr/testify v1.4.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/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208
|
||||||
github.com/xdg-go/scram v1.0.2
|
github.com/xdg-go/scram v1.0.2
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
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/gofrs/flock v0.8.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/tidwall/btree v1.1.0 // indirect
|
github.com/tidwall/btree v1.4.2 // indirect
|
||||||
github.com/tidwall/gjson v1.12.1 // indirect
|
github.com/tidwall/gjson v1.14.3 // indirect
|
||||||
github.com/tidwall/grect v0.1.4 // indirect
|
github.com/tidwall/grect v0.1.4 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // 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 v0.6.1/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
|
||||||
github.com/tidwall/btree v1.1.0 h1:5P+9WU8ui5uhmcg3SoPyTwoI0mVyZ1nps7YQzTZFkYM=
|
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.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 h1:SIyObKAymzLyGhDeIhVk2Yc1/EwfCC75Uyu77CHlVoA=
|
||||||
github.com/tidwall/buntdb v1.2.7/go.mod h1:b6KvZM27x/8JLI5hgRhRu60pa3q0Tz9c50TyD46OHUM=
|
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 h1:XVz684P7X6HCTrdr385yDZWB1zt/n20ZNG3M1iGyFm4=
|
||||||
github.com/tidwall/buntdb v1.2.9/go.mod h1:IwyGSvvDg6hnKSIhtdZ0AqhCZGH8ukdtCAzaP8fI1X4=
|
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 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
|
||||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
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 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
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 h1:z9YwQAMUxVSBde3b7Sl8Da37rffgNfZ6Fq6h9t6KdXE=
|
||||||
github.com/tidwall/grect v0.1.3/go.mod h1:8GMjwh3gPZVpLBI/jDz9uslCe0dpxRpWDdtN0lWAS/E=
|
github.com/tidwall/grect v0.1.3/go.mod h1:8GMjwh3gPZVpLBI/jDz9uslCe0dpxRpWDdtN0lWAS/E=
|
||||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
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)
|
[![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.
|
An efficient [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+*
|
|
||||||
|
|
||||||
## Features
|
## 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.
|
- 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.
|
- [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
|
```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
|
```go
|
||||||
package main
|
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
|
## Performance
|
||||||
|
|
||||||
This implementation was designed with performance in mind.
|
See [tidwall/btree-benchmark](https://github.com/tidwall/btree-benchmark) for benchmark numbers.
|
||||||
|
|
||||||
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)*
|
|
||||||
|
|
||||||
## Contact
|
## 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.
|
// license that can be found in the LICENSE file.
|
||||||
package btree
|
package btree
|
||||||
|
|
||||||
import btree "github.com/tidwall/btree/internal"
|
|
||||||
|
|
||||||
type BTree struct {
|
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
|
// 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 {
|
if less == nil {
|
||||||
panic("nil less")
|
panic("nil less")
|
||||||
}
|
}
|
||||||
return &BTree{
|
return &BTree{
|
||||||
base: btree.NewOptions(btree.Options{
|
base: NewBTreeG(less),
|
||||||
Context: 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,
|
// This is useful for when you do not need the BTree to manage the locking,
|
||||||
// but would rather do it yourself.
|
// 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 {
|
if less == nil {
|
||||||
panic("nil less")
|
panic("nil less")
|
||||||
}
|
}
|
||||||
return &BTree{
|
return &BTree{
|
||||||
base: btree.NewOptions(btree.Options{
|
base: NewBTreeGOptions(less,
|
||||||
Context: less,
|
Options{
|
||||||
NoLocks: true,
|
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
|
// Less is a convenience function that performs a comparison of two items
|
||||||
// using the same "less" function provided to New.
|
// 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)
|
return tr.base.Less(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set or replace a value for a key
|
// 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)
|
return tr.SetHint(item, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHint sets or replace a value for a key using a path hint
|
// 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 {
|
if item == nil {
|
||||||
panic("nil item")
|
panic("nil item")
|
||||||
}
|
}
|
||||||
@ -65,13 +59,15 @@ func (tr *BTree) SetHint(item interface{}, hint *PathHint) (prev interface{}) {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a value for key
|
// Get a value for key.
|
||||||
func (tr *BTree) Get(key interface{}) interface{} {
|
// Returns nil if the key was not found.
|
||||||
|
func (tr *BTree) Get(key any) any {
|
||||||
return tr.GetHint(key, nil)
|
return tr.GetHint(key, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHint gets a value for key using a path hint
|
// GetHint gets a value for key using a path hint.
|
||||||
func (tr *BTree) GetHint(key interface{}, hint *PathHint) interface{} {
|
// Returns nil if the item was not found.
|
||||||
|
func (tr *BTree) GetHint(key any, hint *PathHint) (value any) {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -87,13 +83,15 @@ func (tr *BTree) Len() int {
|
|||||||
return tr.base.Len()
|
return tr.base.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a value for a key
|
// Delete an item for a key.
|
||||||
func (tr *BTree) Delete(key interface{}) interface{} {
|
// 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)
|
return tr.DeleteHint(key, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHint deletes a value for a key using a path hint
|
// 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 {
|
if key == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -107,7 +105,7 @@ func (tr *BTree) DeleteHint(key interface{}, hint *PathHint) interface{} {
|
|||||||
// Ascend the tree within the range [pivot, last]
|
// Ascend the tree within the range [pivot, last]
|
||||||
// Pass nil for pivot to scan all item in ascending order
|
// Pass nil for pivot to scan all item in ascending order
|
||||||
// Return false to stop iterating
|
// 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 {
|
if pivot == nil {
|
||||||
tr.base.Scan(iter)
|
tr.base.Scan(iter)
|
||||||
} else {
|
} else {
|
||||||
@ -118,7 +116,7 @@ func (tr *BTree) Ascend(pivot interface{}, iter func(item interface{}) bool) {
|
|||||||
// Descend the tree within the range [pivot, first]
|
// Descend the tree within the range [pivot, first]
|
||||||
// Pass nil for pivot to scan all item in descending order
|
// Pass nil for pivot to scan all item in descending order
|
||||||
// Return false to stop iterating
|
// 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 {
|
if pivot == nil {
|
||||||
tr.base.Reverse(iter)
|
tr.base.Reverse(iter)
|
||||||
} else {
|
} else {
|
||||||
@ -127,7 +125,9 @@ func (tr *BTree) Descend(pivot interface{}, iter func(item interface{}) bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load is for bulk loading pre-sorted items
|
// 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 {
|
if item == nil {
|
||||||
panic("nil item")
|
panic("nil item")
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ func (tr *BTree) Load(item interface{}) interface{} {
|
|||||||
|
|
||||||
// Min returns the minimum item in tree.
|
// Min returns the minimum item in tree.
|
||||||
// Returns nil if the tree has no items.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *BTree) Min() interface{} {
|
func (tr *BTree) Min() any {
|
||||||
v, ok := tr.base.Min()
|
v, ok := tr.base.Min()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -150,7 +150,7 @@ func (tr *BTree) Min() interface{} {
|
|||||||
|
|
||||||
// Max returns the maximum item in tree.
|
// Max returns the maximum item in tree.
|
||||||
// Returns nil if the tree has no items.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *BTree) Max() interface{} {
|
func (tr *BTree) Max() any {
|
||||||
v, ok := tr.base.Max()
|
v, ok := tr.base.Max()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -160,7 +160,7 @@ func (tr *BTree) Max() interface{} {
|
|||||||
|
|
||||||
// PopMin removes the minimum item in tree and returns it.
|
// PopMin removes the minimum item in tree and returns it.
|
||||||
// Returns nil if the tree has no items.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *BTree) PopMin() interface{} {
|
func (tr *BTree) PopMin() any {
|
||||||
v, ok := tr.base.PopMin()
|
v, ok := tr.base.PopMin()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -168,9 +168,9 @@ func (tr *BTree) PopMin() interface{} {
|
|||||||
return v
|
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.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *BTree) PopMax() interface{} {
|
func (tr *BTree) PopMax() any {
|
||||||
v, ok := tr.base.PopMax()
|
v, ok := tr.base.PopMax()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -180,7 +180,7 @@ func (tr *BTree) PopMax() interface{} {
|
|||||||
|
|
||||||
// GetAt returns the value at index.
|
// GetAt returns the value at index.
|
||||||
// Return nil if the tree is empty or the index is out of bounds.
|
// 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)
|
v, ok := tr.base.GetAt(index)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -190,7 +190,7 @@ func (tr *BTree) GetAt(index int) interface{} {
|
|||||||
|
|
||||||
// DeleteAt deletes the item at index.
|
// DeleteAt deletes the item at index.
|
||||||
// Return nil if the tree is empty or the index is out of bounds.
|
// 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)
|
v, ok := tr.base.DeleteAt(index)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -206,8 +206,8 @@ func (tr *BTree) Height() int {
|
|||||||
|
|
||||||
// Walk iterates over all items in tree, in order.
|
// Walk iterates over all items in tree, in order.
|
||||||
// The items param will contain one or more items.
|
// The items param will contain one or more items.
|
||||||
func (tr *BTree) Walk(iter func(items []interface{})) {
|
func (tr *BTree) Walk(iter func(items []any)) {
|
||||||
tr.base.Walk(func(items []interface{}) bool {
|
tr.base.Walk(func(items []any) bool {
|
||||||
iter(items)
|
iter(items)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -220,7 +220,7 @@ func (tr *BTree) Copy() *BTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Iter struct {
|
type Iter struct {
|
||||||
base btree.Iter
|
base GenericIter[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter returns a read-only iterator.
|
// Iter returns a read-only iterator.
|
||||||
@ -231,7 +231,7 @@ func (tr *BTree) Iter() Iter {
|
|||||||
|
|
||||||
// Seek to item greater-or-equal-to key.
|
// Seek to item greater-or-equal-to key.
|
||||||
// Returns false if there was no item found.
|
// 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)
|
return iter.base.Seek(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +268,6 @@ func (iter *Iter) Prev() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Item returns the current iterator item.
|
// Item returns the current iterator item.
|
||||||
func (iter *Iter) Item() interface{} {
|
func (iter *Iter) Item() any {
|
||||||
return iter.base.Item()
|
return iter.base.Item()
|
||||||
}
|
}
|
||||||
|
@ -1,178 +1,109 @@
|
|||||||
// Copyright 2020 Joshua J Baker. All rights reserved.
|
// Copyright 2020 Joshua J Baker. All rights reserved.
|
||||||
// Use of this source code is governed by an MIT-style license that can be
|
// Use of this source code is governed by an MIT-style
|
||||||
// found in the LICENSE file at https://github.com/tidwall/btree/LICENSE
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// BEGIN PARAMS
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
package btree
|
package btree
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
// degree is the B-Tree degree, which is equal to maximum number of children
|
const (
|
||||||
// pre node times two.
|
degree = 128
|
||||||
// The default is 128, which means each node can have 255 items and 256 child
|
maxItems = degree*2 - 1 // max items per node. max children is +1
|
||||||
// nodes.
|
minItems = maxItems / 2
|
||||||
const degree = 128
|
)
|
||||||
|
|
||||||
// kind is the item type.
|
type BTreeG[T any] struct {
|
||||||
// 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 {
|
|
||||||
mu *sync.RWMutex
|
mu *sync.RWMutex
|
||||||
cow *cow
|
cow uint64
|
||||||
root *node
|
root *node[T]
|
||||||
count int
|
count int
|
||||||
ctx contextKind
|
|
||||||
locks bool
|
locks bool
|
||||||
empty kind
|
less func(a, b T) bool
|
||||||
|
empty T
|
||||||
}
|
}
|
||||||
|
|
||||||
type node struct {
|
type node[T any] struct {
|
||||||
cow *cow
|
cow uint64
|
||||||
count int
|
count int
|
||||||
items []kind
|
items []T
|
||||||
children *[]*node
|
children *[]*node[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
type cow struct {
|
var gcow uint64
|
||||||
_ 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathHint is a utility type used with the *Hint() functions. Hints provide
|
// PathHint is a utility type used with the *Hint() functions. Hints provide
|
||||||
// faster operations for clustered keys.
|
// faster operations for clustered keys.
|
||||||
type bPathHint struct {
|
type PathHint struct {
|
||||||
used [8]bool
|
used [8]bool
|
||||||
path [8]uint8
|
path [8]uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
type bOptions struct {
|
// Options for passing to New when creating a new BTree.
|
||||||
|
type Options struct {
|
||||||
NoLocks bool
|
NoLocks bool
|
||||||
Context contextKind
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new BTree
|
// New returns a new BTree
|
||||||
func bNew() *bTree {
|
func NewBTreeG[T any](less func(a, b T) bool) *BTreeG[T] {
|
||||||
return bNewOptions(bOptions{})
|
return NewBTreeGOptions(less, Options{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func bNewOptions(opts bOptions) *bTree {
|
func NewBTreeGOptions[T any](less func(a, b T) bool, opts Options) *BTreeG[T] {
|
||||||
tr := new(bTree)
|
tr := new(BTreeG[T])
|
||||||
tr.cow = new(cow)
|
tr.cow = atomic.AddUint64(&gcow, 1)
|
||||||
tr.mu = new(sync.RWMutex)
|
tr.mu = new(sync.RWMutex)
|
||||||
tr.ctx = opts.Context
|
tr.less = less
|
||||||
tr.locks = !opts.NoLocks
|
tr.locks = !opts.NoLocks
|
||||||
return tr
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less is a convenience function that performs a comparison of two items
|
// Less is a convenience function that performs a comparison of two items
|
||||||
// using the same "less" function provided to New.
|
// using the same "less" function provided to New.
|
||||||
func (tr *bTree) Less(a, b kind) bool {
|
func (tr *BTreeG[T]) Less(a, b T) bool {
|
||||||
return less(a, b, tr.ctx)
|
return tr.less(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) find(n *node, key kind,
|
func (tr *BTreeG[T]) newNode(leaf bool) *node[T] {
|
||||||
hint *bPathHint, depth int,
|
n := &node[T]{cow: tr.cow}
|
||||||
) (index int, found bool) {
|
if !leaf {
|
||||||
if hint == nil {
|
n.children = new([]*node[T])
|
||||||
// fast path for no hinting
|
}
|
||||||
low := 0
|
return n
|
||||||
high := len(n.items)
|
}
|
||||||
|
|
||||||
|
// 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 {
|
for low < high {
|
||||||
mid := (low + high) / 2
|
h := int(uint(low+high) >> 1)
|
||||||
if !tr.Less(key, n.items[mid]) {
|
if !tr.less(key, n.items[h]) {
|
||||||
low = mid + 1
|
low = h + 1
|
||||||
} else {
|
} 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 - 1, true
|
||||||
}
|
}
|
||||||
return low, false
|
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.
|
// Best case finds the exact match, updates the hint and returns.
|
||||||
// Worst case, updates the low and high bounds to binary search between.
|
// Worst case, updates the low and high bounds to binary search between.
|
||||||
low := 0
|
low := 0
|
||||||
@ -247,17 +178,21 @@ path_match:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetHint sets or replace a value for a key using a path hint
|
// 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) {
|
func (tr *BTreeG[T]) SetHint(item T, hint *PathHint) (prev T, replaced bool) {
|
||||||
if tr.lock() {
|
if tr.locks {
|
||||||
defer tr.unlock()
|
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 {
|
if tr.root == nil {
|
||||||
tr.root = tr.newNode(true)
|
tr.root = tr.newNode(true)
|
||||||
tr.root.items = append([]kind{}, item)
|
tr.root.items = append([]T{}, item)
|
||||||
tr.root.count = 1
|
tr.root.count = 1
|
||||||
tr.count = 1
|
tr.count = 1
|
||||||
return tr.empty, false
|
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)
|
left := tr.cowLoad(&tr.root)
|
||||||
right, median := tr.nodeSplit(left)
|
right, median := tr.nodeSplit(left)
|
||||||
tr.root = tr.newNode(false)
|
tr.root = tr.newNode(false)
|
||||||
*tr.root.children = make([]*node, 0, maxItems+1)
|
*tr.root.children = make([]*node[T], 0, maxItems+1)
|
||||||
*tr.root.children = append([]*node{}, left, right)
|
*tr.root.children = append([]*node[T]{}, left, right)
|
||||||
tr.root.items = append([]kind{}, median)
|
tr.root.items = append([]T{}, median)
|
||||||
tr.root.updateCount()
|
tr.root.updateCount()
|
||||||
return tr.setHint(item, hint)
|
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
|
// 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)
|
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
|
i := maxItems / 2
|
||||||
median = n.items[i]
|
median = n.items[i]
|
||||||
|
|
||||||
// left node
|
const sliceItems = true
|
||||||
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()
|
|
||||||
|
|
||||||
// right node
|
// right node
|
||||||
right = tr.newNode(n.leaf())
|
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:])
|
copy(right.items, n.items[i+1:])
|
||||||
if !n.leaf() {
|
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:])
|
copy(*right.children, (*n.children)[i+1:])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
right.updateCount()
|
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
|
return right, median
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) updateCount() {
|
func (n *node[T]) updateCount() {
|
||||||
n.count = len(n.items)
|
n.count = len(n.items)
|
||||||
if !n.leaf() {
|
if !n.leaf() {
|
||||||
for i := 0; i < len(*n.children); i++ {
|
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"
|
// called outside of heavy copy-on-write situations. Marking it "noinline"
|
||||||
// allows for the parent cowLoad to be inlined.
|
// allows for the parent cowLoad to be inlined.
|
||||||
// go:noinline
|
// go:noinline
|
||||||
func (tr *bTree) copy(n *node) *node {
|
func (tr *BTreeG[T]) copy(n *node[T]) *node[T] {
|
||||||
n2 := new(node)
|
n2 := new(node[T])
|
||||||
n2.cow = tr.cow
|
n2.cow = tr.cow
|
||||||
n2.count = n.count
|
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)
|
copy(n2.items, n.items)
|
||||||
if !n.leaf() {
|
if !n.leaf() {
|
||||||
n2.children = new([]*node)
|
n2.children = new([]*node[T])
|
||||||
*n2.children = make([]*node, len(*n.children), maxItems+1)
|
*n2.children = make([]*node[T], len(*n.children), maxItems+1)
|
||||||
copy(*n2.children, *n.children)
|
copy(*n2.children, *n.children)
|
||||||
}
|
}
|
||||||
return n2
|
return n2
|
||||||
}
|
}
|
||||||
|
|
||||||
// cowLoad loads the provided node and, if needed, performs a copy-on-write.
|
// 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 {
|
if (*cn).cow != tr.cow {
|
||||||
*cn = tr.copy(*cn)
|
*cn = tr.copy(*cn)
|
||||||
}
|
}
|
||||||
return *cn
|
return *cn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) nodeSet(cn **node, item kind,
|
func (tr *BTreeG[T]) nodeSet(cn **node[T], item T,
|
||||||
hint *bPathHint, depth int,
|
hint *PathHint, depth int,
|
||||||
) (prev kind, replaced bool, split bool) {
|
) (prev T, replaced bool, split bool) {
|
||||||
n := tr.cowLoad(cn)
|
if (*cn).cow != tr.cow {
|
||||||
i, found := tr.find(n, item, hint, depth)
|
*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 {
|
if found {
|
||||||
prev = n.items[i]
|
prev = n.items[i]
|
||||||
n.items[i] = item
|
n.items[i] = item
|
||||||
@ -388,7 +354,7 @@ func (tr *bTree) nodeSet(cn **node, item kind,
|
|||||||
return prev, replaced, false
|
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() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
defer tr.runlock()
|
||||||
}
|
}
|
||||||
@ -398,7 +364,7 @@ func (tr *bTree) Scan(iter func(item kind) bool) {
|
|||||||
tr.root.scan(iter)
|
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() {
|
if n.leaf() {
|
||||||
for i := 0; i < len(n.items); i++ {
|
for i := 0; i < len(n.items); i++ {
|
||||||
if !iter(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
|
// 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)
|
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
|
// 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() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
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 {
|
if tr.root == nil {
|
||||||
return tr.empty, false
|
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
|
// Len returns the number of items in the tree
|
||||||
func (tr *bTree) Len() int {
|
func (tr *BTreeG[T]) Len() int {
|
||||||
return tr.count
|
return tr.count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a value for a key
|
// Delete a value for a key and returns the deleted value.
|
||||||
func (tr *bTree) Delete(key kind) (kind, bool) {
|
// 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)
|
return tr.DeleteHint(key, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHint deletes a value for a key using a path hint
|
// DeleteHint deletes a value for a key using a path hint and returns the
|
||||||
func (tr *bTree) DeleteHint(key kind, hint *bPathHint) (kind, bool) {
|
// 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() {
|
if tr.lock() {
|
||||||
defer tr.unlock()
|
defer tr.unlock()
|
||||||
}
|
}
|
||||||
return tr.deleteHint(key, hint)
|
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 {
|
if tr.root == nil {
|
||||||
return tr.empty, false
|
return tr.empty, false
|
||||||
}
|
}
|
||||||
@ -482,9 +472,9 @@ func (tr *bTree) deleteHint(key kind, hint *bPathHint) (kind, bool) {
|
|||||||
return prev, true
|
return prev, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) delete(cn **node, max bool, key kind,
|
func (tr *BTreeG[T]) delete(cn **node[T], max bool, key T,
|
||||||
hint *bPathHint, depth int,
|
hint *PathHint, depth int,
|
||||||
) (kind, bool) {
|
) (T, bool) {
|
||||||
n := tr.cowLoad(cn)
|
n := tr.cowLoad(cn)
|
||||||
var i int
|
var i int
|
||||||
var found bool
|
var found bool
|
||||||
@ -506,7 +496,7 @@ func (tr *bTree) delete(cn **node, max bool, key kind,
|
|||||||
return tr.empty, false
|
return tr.empty, false
|
||||||
}
|
}
|
||||||
|
|
||||||
var prev kind
|
var prev T
|
||||||
var deleted bool
|
var deleted bool
|
||||||
if found {
|
if found {
|
||||||
if max {
|
if max {
|
||||||
@ -529,13 +519,12 @@ func (tr *bTree) delete(cn **node, max bool, key kind,
|
|||||||
tr.nodeRebalance(n, i)
|
tr.nodeRebalance(n, i)
|
||||||
}
|
}
|
||||||
return prev, true
|
return prev, true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeRebalance rebalances the child nodes following a delete operation.
|
// nodeRebalance rebalances the child nodes following a delete operation.
|
||||||
// Provide the index of the child node with the number of items that fell
|
// Provide the index of the child node with the number of items that fell
|
||||||
// below minItems.
|
// 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) {
|
if i == len(n.items) {
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
@ -618,7 +607,7 @@ func (tr *bTree) nodeRebalance(n *node, i int) {
|
|||||||
// Ascend the tree within the range [pivot, last]
|
// Ascend the tree within the range [pivot, last]
|
||||||
// Pass nil for pivot to scan all item in ascending order
|
// Pass nil for pivot to scan all item in ascending order
|
||||||
// Return false to stop iterating
|
// 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() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
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
|
// The return value of this function determines whether we should keep iterating
|
||||||
// upon this functions return.
|
// upon this functions return.
|
||||||
func (tr *bTree) ascend(n *node, pivot kind,
|
func (tr *BTreeG[T]) ascend(n *node[T], pivot T,
|
||||||
hint *bPathHint, depth int, iter func(item kind) bool,
|
hint *PathHint, depth int, iter func(item T) bool,
|
||||||
) bool {
|
) bool {
|
||||||
i, found := tr.find(n, pivot, hint, depth)
|
i, found := tr.find(n, pivot, hint, depth)
|
||||||
if !found {
|
if !found {
|
||||||
@ -658,7 +647,7 @@ func (tr *bTree) ascend(n *node, pivot kind,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) Reverse(iter func(item kind) bool) {
|
func (tr *BTreeG[T]) Reverse(iter func(item T) bool) {
|
||||||
if tr.rlock() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
defer tr.runlock()
|
||||||
}
|
}
|
||||||
@ -668,7 +657,7 @@ func (tr *bTree) Reverse(iter func(item kind) bool) {
|
|||||||
tr.root.reverse(iter)
|
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() {
|
if n.leaf() {
|
||||||
for i := len(n.items) - 1; i >= 0; i-- {
|
for i := len(n.items) - 1; i >= 0; i-- {
|
||||||
if !iter(n.items[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]
|
// Descend the tree within the range [pivot, first]
|
||||||
// Pass nil for pivot to scan all item in descending order
|
// Pass nil for pivot to scan all item in descending order
|
||||||
// Return false to stop iterating
|
// 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() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
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)
|
tr.descend(tr.root, pivot, nil, 0, iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) descend(n *node, pivot kind,
|
func (tr *BTreeG[T]) descend(n *node[T], pivot T,
|
||||||
hint *bPathHint, depth int, iter func(item kind) bool,
|
hint *PathHint, depth int, iter func(item T) bool,
|
||||||
) bool {
|
) bool {
|
||||||
i, found := tr.find(n, pivot, hint, depth)
|
i, found := tr.find(n, pivot, hint, depth)
|
||||||
if !found {
|
if !found {
|
||||||
@ -730,7 +719,7 @@ func (tr *bTree) descend(n *node, pivot kind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load is for bulk loading pre-sorted items
|
// 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() {
|
if tr.lock() {
|
||||||
defer tr.unlock()
|
defer tr.unlock()
|
||||||
}
|
}
|
||||||
@ -765,8 +754,8 @@ func (tr *bTree) Load(item kind) (kind, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Min returns the minimum item in tree.
|
// Min returns the minimum item in tree.
|
||||||
// Returns nil if the tree has no items.
|
// Returns nil if the treex has no items.
|
||||||
func (tr *bTree) Min() (kind, bool) {
|
func (tr *BTreeG[T]) Min() (T, bool) {
|
||||||
if tr.rlock() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
defer tr.runlock()
|
||||||
}
|
}
|
||||||
@ -784,7 +773,7 @@ func (tr *bTree) Min() (kind, bool) {
|
|||||||
|
|
||||||
// Max returns the maximum item in tree.
|
// Max returns the maximum item in tree.
|
||||||
// Returns nil if the tree has no items.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *bTree) Max() (kind, bool) {
|
func (tr *BTreeG[T]) Max() (T, bool) {
|
||||||
if tr.rlock() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
defer tr.runlock()
|
||||||
}
|
}
|
||||||
@ -802,7 +791,7 @@ func (tr *bTree) Max() (kind, bool) {
|
|||||||
|
|
||||||
// PopMin removes the minimum item in tree and returns it.
|
// PopMin removes the minimum item in tree and returns it.
|
||||||
// Returns nil if the tree has no items.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *bTree) PopMin() (kind, bool) {
|
func (tr *BTreeG[T]) PopMin() (T, bool) {
|
||||||
if tr.lock() {
|
if tr.lock() {
|
||||||
defer tr.unlock()
|
defer tr.unlock()
|
||||||
}
|
}
|
||||||
@ -810,7 +799,7 @@ func (tr *bTree) PopMin() (kind, bool) {
|
|||||||
return tr.empty, false
|
return tr.empty, false
|
||||||
}
|
}
|
||||||
n := tr.cowLoad(&tr.root)
|
n := tr.cowLoad(&tr.root)
|
||||||
var item kind
|
var item T
|
||||||
for {
|
for {
|
||||||
n.count-- // optimistically update counts
|
n.count-- // optimistically update counts
|
||||||
if n.leaf() {
|
if n.leaf() {
|
||||||
@ -841,9 +830,9 @@ func (tr *bTree) PopMin() (kind, bool) {
|
|||||||
return tr.deleteHint(item, nil)
|
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.
|
// Returns nil if the tree has no items.
|
||||||
func (tr *bTree) PopMax() (kind, bool) {
|
func (tr *BTreeG[T]) PopMax() (T, bool) {
|
||||||
if tr.lock() {
|
if tr.lock() {
|
||||||
defer tr.unlock()
|
defer tr.unlock()
|
||||||
}
|
}
|
||||||
@ -851,7 +840,7 @@ func (tr *bTree) PopMax() (kind, bool) {
|
|||||||
return tr.empty, false
|
return tr.empty, false
|
||||||
}
|
}
|
||||||
n := tr.cowLoad(&tr.root)
|
n := tr.cowLoad(&tr.root)
|
||||||
var item kind
|
var item T
|
||||||
for {
|
for {
|
||||||
n.count-- // optimistically update counts
|
n.count-- // optimistically update counts
|
||||||
if n.leaf() {
|
if n.leaf() {
|
||||||
@ -883,7 +872,7 @@ func (tr *bTree) PopMax() (kind, bool) {
|
|||||||
|
|
||||||
// GetAt returns the value at index.
|
// GetAt returns the value at index.
|
||||||
// Return nil if the tree is empty or the index is out of bounds.
|
// 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() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
defer tr.runlock()
|
||||||
}
|
}
|
||||||
@ -910,7 +899,7 @@ func (tr *bTree) GetAt(index int) (kind, bool) {
|
|||||||
|
|
||||||
// DeleteAt deletes the item at index.
|
// DeleteAt deletes the item at index.
|
||||||
// Return nil if the tree is empty or the index is out of bounds.
|
// 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() {
|
if tr.lock() {
|
||||||
defer tr.unlock()
|
defer tr.unlock()
|
||||||
}
|
}
|
||||||
@ -919,7 +908,7 @@ func (tr *bTree) DeleteAt(index int) (kind, bool) {
|
|||||||
}
|
}
|
||||||
var pathbuf [8]uint8 // track the path
|
var pathbuf [8]uint8 // track the path
|
||||||
path := pathbuf[:0]
|
path := pathbuf[:0]
|
||||||
var item kind
|
var item T
|
||||||
n := tr.cowLoad(&tr.root)
|
n := tr.cowLoad(&tr.root)
|
||||||
outer:
|
outer:
|
||||||
for {
|
for {
|
||||||
@ -955,7 +944,7 @@ outer:
|
|||||||
n = tr.cowLoad(&(*n.children)[i])
|
n = tr.cowLoad(&(*n.children)[i])
|
||||||
}
|
}
|
||||||
// revert the counts
|
// revert the counts
|
||||||
var hint bPathHint
|
var hint PathHint
|
||||||
n = tr.root
|
n = tr.root
|
||||||
for i := 0; i < len(path); i++ {
|
for i := 0; i < len(path); i++ {
|
||||||
if i < len(hint.path) {
|
if i < len(hint.path) {
|
||||||
@ -972,7 +961,7 @@ outer:
|
|||||||
|
|
||||||
// Height returns the height of the tree.
|
// Height returns the height of the tree.
|
||||||
// Returns zero if tree has no items.
|
// Returns zero if tree has no items.
|
||||||
func (tr *bTree) Height() int {
|
func (tr *BTreeG[T]) Height() int {
|
||||||
if tr.rlock() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
defer tr.runlock()
|
||||||
}
|
}
|
||||||
@ -992,7 +981,7 @@ func (tr *bTree) Height() int {
|
|||||||
|
|
||||||
// Walk iterates over all items in tree, in order.
|
// Walk iterates over all items in tree, in order.
|
||||||
// The items param will contain one or more items.
|
// 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() {
|
if tr.rlock() {
|
||||||
defer tr.runlock()
|
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 n.leaf() {
|
||||||
if !iter(n.items) {
|
if !iter(n.items) {
|
||||||
return false
|
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
|
// Copy the tree. This is a copy-on-write operation and is very fast because
|
||||||
// it only performs a shadowed copy.
|
// it only performs a shadowed copy.
|
||||||
func (tr *bTree) Copy() *bTree {
|
func (tr *BTreeG[T]) Copy() *BTreeG[T] {
|
||||||
if tr.lock() {
|
if tr.lock() {
|
||||||
defer tr.unlock()
|
defer tr.unlock()
|
||||||
}
|
}
|
||||||
tr.cow = new(cow)
|
tr.cow = atomic.AddUint64(&gcow, 1)
|
||||||
tr2 := new(bTree)
|
tr2 := new(BTreeG[T])
|
||||||
*tr2 = *tr
|
*tr2 = *tr
|
||||||
tr2.mu = new(sync.RWMutex)
|
tr2.mu = new(sync.RWMutex)
|
||||||
tr2.cow = new(cow)
|
tr2.cow = atomic.AddUint64(&gcow, 1)
|
||||||
return tr2
|
return tr2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) lock() bool {
|
func (tr *BTreeG[T]) lock() bool {
|
||||||
if tr.locks {
|
if tr.locks {
|
||||||
tr.mu.Lock()
|
tr.mu.Lock()
|
||||||
}
|
}
|
||||||
return tr.locks
|
return tr.locks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) unlock() {
|
func (tr *BTreeG[T]) unlock() {
|
||||||
tr.mu.Unlock()
|
tr.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) rlock() bool {
|
func (tr *BTreeG[T]) rlock() bool {
|
||||||
if tr.locks {
|
if tr.locks {
|
||||||
tr.mu.RLock()
|
tr.mu.RLock()
|
||||||
}
|
}
|
||||||
return tr.locks
|
return tr.locks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *bTree) runlock() {
|
func (tr *BTreeG[T]) runlock() {
|
||||||
tr.mu.RUnlock()
|
tr.mu.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter represents an iterator
|
// Iter represents an iterator
|
||||||
type bIter struct {
|
type GenericIter[T any] struct {
|
||||||
tr *bTree
|
tr *BTreeG[T]
|
||||||
locked bool
|
locked bool
|
||||||
seeked bool
|
seeked bool
|
||||||
atstart bool
|
atstart bool
|
||||||
atend bool
|
atend bool
|
||||||
stack []iterStackItem
|
stack []genericIterStackItem[T]
|
||||||
item kind
|
item T
|
||||||
}
|
}
|
||||||
|
|
||||||
type iterStackItem struct {
|
type genericIterStackItem[T any] struct {
|
||||||
n *node
|
n *node[T]
|
||||||
i int
|
i int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter returns a read-only iterator.
|
// Iter returns a read-only iterator.
|
||||||
// The Release method must be called finished with iterator.
|
// The Release method must be called finished with iterator.
|
||||||
func (tr *bTree) Iter() bIter {
|
func (tr *BTreeG[T]) Iter() GenericIter[T] {
|
||||||
var iter bIter
|
var iter GenericIter[T]
|
||||||
iter.tr = tr
|
iter.tr = tr
|
||||||
iter.locked = tr.rlock()
|
iter.locked = tr.rlock()
|
||||||
return iter
|
return iter
|
||||||
@ -1081,7 +1070,7 @@ func (tr *bTree) Iter() bIter {
|
|||||||
|
|
||||||
// Seek to item greater-or-equal-to key.
|
// Seek to item greater-or-equal-to key.
|
||||||
// Returns false if there was no item found.
|
// 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 {
|
if iter.tr == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1093,16 +1082,14 @@ func (iter *bIter) Seek(key kind) bool {
|
|||||||
n := iter.tr.root
|
n := iter.tr.root
|
||||||
for {
|
for {
|
||||||
i, found := iter.tr.find(n, key, nil, 0)
|
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 {
|
if found {
|
||||||
|
iter.item = n.items[i]
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if n.leaf() {
|
if n.leaf() {
|
||||||
if i == len(n.items) {
|
iter.stack[len(iter.stack)-1].i--
|
||||||
iter.stack = iter.stack[:0]
|
return iter.Next()
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
n = (*n.children)[i]
|
n = (*n.children)[i]
|
||||||
}
|
}
|
||||||
@ -1110,7 +1097,7 @@ func (iter *bIter) Seek(key kind) bool {
|
|||||||
|
|
||||||
// First moves iterator to first item in tree.
|
// First moves iterator to first item in tree.
|
||||||
// Returns false if the tree is empty.
|
// Returns false if the tree is empty.
|
||||||
func (iter *bIter) First() bool {
|
func (iter *GenericIter[T]) First() bool {
|
||||||
if iter.tr == nil {
|
if iter.tr == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1123,7 +1110,7 @@ func (iter *bIter) First() bool {
|
|||||||
}
|
}
|
||||||
n := iter.tr.root
|
n := iter.tr.root
|
||||||
for {
|
for {
|
||||||
iter.stack = append(iter.stack, iterStackItem{n, 0})
|
iter.stack = append(iter.stack, genericIterStackItem[T]{n, 0})
|
||||||
if n.leaf() {
|
if n.leaf() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1136,7 +1123,7 @@ func (iter *bIter) First() bool {
|
|||||||
|
|
||||||
// Last moves iterator to last item in tree.
|
// Last moves iterator to last item in tree.
|
||||||
// Returns false if the tree is empty.
|
// Returns false if the tree is empty.
|
||||||
func (iter *bIter) Last() bool {
|
func (iter *GenericIter[T]) Last() bool {
|
||||||
if iter.tr == nil {
|
if iter.tr == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1147,7 +1134,7 @@ func (iter *bIter) Last() bool {
|
|||||||
}
|
}
|
||||||
n := iter.tr.root
|
n := iter.tr.root
|
||||||
for {
|
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() {
|
if n.leaf() {
|
||||||
iter.stack[len(iter.stack)-1].i--
|
iter.stack[len(iter.stack)-1].i--
|
||||||
break
|
break
|
||||||
@ -1159,9 +1146,8 @@ func (iter *bIter) Last() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// First moves iterator to first item in tree.
|
// Release the iterator.
|
||||||
// Returns false if the tree is empty.
|
func (iter *GenericIter[T]) Release() {
|
||||||
func (iter *bIter) Release() {
|
|
||||||
if iter.tr == nil {
|
if iter.tr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1176,7 +1162,7 @@ func (iter *bIter) Release() {
|
|||||||
// Next moves iterator to the next item in iterator.
|
// Next moves iterator to the next item in iterator.
|
||||||
// Returns false if the tree is empty or the iterator is at the end of
|
// Returns false if the tree is empty or the iterator is at the end of
|
||||||
// the tree.
|
// the tree.
|
||||||
func (iter *bIter) Next() bool {
|
func (iter *GenericIter[T]) Next() bool {
|
||||||
if iter.tr == nil {
|
if iter.tr == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1208,7 +1194,7 @@ func (iter *bIter) Next() bool {
|
|||||||
} else {
|
} else {
|
||||||
n := (*s.n.children)[s.i]
|
n := (*s.n.children)[s.i]
|
||||||
for {
|
for {
|
||||||
iter.stack = append(iter.stack, iterStackItem{n, 0})
|
iter.stack = append(iter.stack, genericIterStackItem[T]{n, 0})
|
||||||
if n.leaf() {
|
if n.leaf() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1223,7 +1209,7 @@ func (iter *bIter) Next() bool {
|
|||||||
// Prev moves iterator to the previous item in iterator.
|
// Prev moves iterator to the previous item in iterator.
|
||||||
// Returns false if the tree is empty or the iterator is at the beginning of
|
// Returns false if the tree is empty or the iterator is at the beginning of
|
||||||
// the tree.
|
// the tree.
|
||||||
func (iter *bIter) Prev() bool {
|
func (iter *GenericIter[T]) Prev() bool {
|
||||||
if iter.tr == nil {
|
if iter.tr == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1256,7 +1242,7 @@ func (iter *bIter) Prev() bool {
|
|||||||
} else {
|
} else {
|
||||||
n := (*s.n.children)[s.i]
|
n := (*s.n.children)[s.i]
|
||||||
for {
|
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() {
|
if n.leaf() {
|
||||||
iter.stack[len(iter.stack)-1].i--
|
iter.stack[len(iter.stack)-1].i--
|
||||||
break
|
break
|
||||||
@ -1270,6 +1256,48 @@ func (iter *bIter) Prev() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Item returns the current iterator item.
|
// Item returns the current iterator item.
|
||||||
func (iter *bIter) Item() kind {
|
func (iter *GenericIter[T]) Item() T {
|
||||||
return iter.item
|
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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
@ -749,13 +750,13 @@ func (db *DB) Shrink() error {
|
|||||||
if err := db.file.Close(); err != nil {
|
if err := db.file.Close(); err != nil {
|
||||||
return err
|
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 {
|
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)
|
db.file, err = os.OpenFile(fname, os.O_CREATE|os.O_RDWR, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panicErr(err)
|
||||||
}
|
}
|
||||||
pos, err := db.file.Seek(0, 2)
|
pos, err := db.file.Seek(0, 2)
|
||||||
if err != nil {
|
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.
|
// readLoad reads from the reader and loads commands into the database.
|
||||||
// modTime is the modified time of the reader, should be no greater than
|
// modTime is the modified time of the reader, should be no greater than
|
||||||
// the current time.Now().
|
// the current time.Now().
|
||||||
@ -1209,10 +1214,10 @@ func (tx *Tx) Commit() error {
|
|||||||
// should be killed to avoid corrupting the file.
|
// should be killed to avoid corrupting the file.
|
||||||
pos, err := tx.db.file.Seek(-int64(n), 1)
|
pos, err := tx.db.file.Seek(-int64(n), 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panicErr(err)
|
||||||
}
|
}
|
||||||
if err := tx.db.file.Truncate(pos); err != nil {
|
if err := tx.db.file.Truncate(pos); err != nil {
|
||||||
panic(err)
|
panicErr(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.rollbackInner()
|
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.
|
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
|
Getting Started
|
||||||
===============
|
===============
|
||||||
|
|
||||||
@ -204,6 +208,9 @@ There are currently the following built-in modifiers:
|
|||||||
- `@join`: Joins multiple objects into a single object.
|
- `@join`: Joins multiple objects into a single object.
|
||||||
- `@keys`: Returns an array of keys for an object.
|
- `@keys`: Returns an array of keys for an object.
|
||||||
- `@values`: Returns an array of values 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
|
### 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.
|
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
|
## Example
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`.
|
|||||||
fav\.movie "Deer Hunter"
|
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
|
||||||
// Go
|
// Go
|
||||||
@ -238,6 +238,9 @@ There are currently the following built-in modifiers:
|
|||||||
- `@join`: Joins multiple objects into a single object.
|
- `@join`: Joins multiple objects into a single object.
|
||||||
- `@keys`: Returns an array of keys for an object.
|
- `@keys`: Returns an array of keys for an object.
|
||||||
- `@values`: Returns an array of values 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
|
#### 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
|
package gjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -214,6 +213,11 @@ func (t Result) IsArray() bool {
|
|||||||
return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['
|
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.
|
// ForEach iterates through values.
|
||||||
// If the result represents a non-existent value, then no values will be
|
// 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
|
// 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] == '.' {
|
if path[i] == '.' {
|
||||||
r.part = 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.pipe = path[i+1:]
|
||||||
r.piped = true
|
r.piped = true
|
||||||
} else {
|
} else {
|
||||||
@ -932,8 +936,23 @@ right:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// peek at the next byte and see if it's a '@', '[', or '{'.
|
// peek at the next byte and see if it's a '@', '[', or '{'.
|
||||||
func isDotPiperChar(c byte) bool {
|
func isDotPiperChar(s string) bool {
|
||||||
return !DisableModifiers && (c == '@' || c == '[' || c == '{')
|
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 {
|
type objectPathResult struct {
|
||||||
@ -955,7 +974,7 @@ func parseObjectPath(path string) (r objectPathResult) {
|
|||||||
}
|
}
|
||||||
if path[i] == '.' {
|
if path[i] == '.' {
|
||||||
r.part = 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.pipe = path[i+1:]
|
||||||
r.piped = true
|
r.piped = true
|
||||||
} else {
|
} else {
|
||||||
@ -985,7 +1004,7 @@ func parseObjectPath(path string) (r objectPathResult) {
|
|||||||
continue
|
continue
|
||||||
} else if path[i] == '.' {
|
} else if path[i] == '.' {
|
||||||
r.part = string(epart)
|
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.pipe = path[i+1:]
|
||||||
r.piped = true
|
r.piped = true
|
||||||
} else {
|
} else {
|
||||||
@ -1819,17 +1838,64 @@ func isSimpleName(component string) bool {
|
|||||||
return true
|
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++ {
|
for i := 0; i < len(s); i++ {
|
||||||
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
|
if s[i] < ' ' {
|
||||||
d, _ := json.Marshal(s)
|
dst = append(dst, '\\')
|
||||||
return append(dst, string(d)...)
|
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, '"')
|
return append(dst, '"')
|
||||||
dst = append(dst, s...)
|
|
||||||
dst = append(dst, '"')
|
|
||||||
return dst
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type parseContext struct {
|
type parseContext struct {
|
||||||
@ -1919,14 +1985,14 @@ func Get(json, path string) Result {
|
|||||||
if sub.name[0] == '"' && Valid(sub.name) {
|
if sub.name[0] == '"' && Valid(sub.name) {
|
||||||
b = append(b, sub.name...)
|
b = append(b, sub.name...)
|
||||||
} else {
|
} else {
|
||||||
b = appendJSONString(b, sub.name)
|
b = AppendJSONString(b, sub.name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
last := nameOfLast(sub.path)
|
last := nameOfLast(sub.path)
|
||||||
if isSimpleName(last) {
|
if isSimpleName(last) {
|
||||||
b = appendJSONString(b, last)
|
b = AppendJSONString(b, last)
|
||||||
} else {
|
} else {
|
||||||
b = appendJSONString(b, "_")
|
b = AppendJSONString(b, "_")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b = append(b, ':')
|
b = append(b, ':')
|
||||||
@ -2669,6 +2735,9 @@ var modifiers = map[string]func(json, arg string) string{
|
|||||||
"valid": modValid,
|
"valid": modValid,
|
||||||
"keys": modKeys,
|
"keys": modKeys,
|
||||||
"values": modValues,
|
"values": modValues,
|
||||||
|
"tostr": modToStr,
|
||||||
|
"fromstr": modFromStr,
|
||||||
|
"group": modGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddModifier binds a custom modifier command to the GJSON syntax.
|
// AddModifier binds a custom modifier command to the GJSON syntax.
|
||||||
@ -2954,6 +3023,56 @@ func modValid(json, arg string) string {
|
|||||||
return json
|
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
|
// stringHeader instead of reflect.StringHeader
|
||||||
type stringHeader struct {
|
type stringHeader struct {
|
||||||
data unsafe.Pointer
|
data unsafe.Pointer
|
||||||
@ -3088,6 +3207,20 @@ func revSquash(json string) string {
|
|||||||
return json
|
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 {
|
func (t Result) Paths(json string) []string {
|
||||||
if t.Indexes == nil {
|
if t.Indexes == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -3103,8 +3236,20 @@ func (t Result) Paths(json string) []string {
|
|||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path returns the original GJSON path for Result.
|
// Path returns the original GJSON path for a Result where the Result came
|
||||||
// The json param must be the original JSON used when calling Get.
|
// 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 {
|
func (t Result) Path(json string) string {
|
||||||
var path []byte
|
var path []byte
|
||||||
var comps []string // raw components
|
var comps []string // raw components
|
||||||
|
11
vendor/modules.txt
vendored
11
vendor/modules.txt
vendored
@ -45,14 +45,13 @@ github.com/okzk/sdnotify
|
|||||||
## explicit
|
## explicit
|
||||||
# github.com/stretchr/testify v1.4.0
|
# github.com/stretchr/testify v1.4.0
|
||||||
## explicit
|
## explicit
|
||||||
# github.com/tidwall/btree v1.1.0
|
# github.com/tidwall/btree v1.4.2
|
||||||
## explicit; go 1.16
|
## explicit; go 1.18
|
||||||
github.com/tidwall/btree
|
github.com/tidwall/btree
|
||||||
github.com/tidwall/btree/internal
|
# github.com/tidwall/buntdb v1.2.10
|
||||||
# github.com/tidwall/buntdb v1.2.9
|
## explicit; go 1.18
|
||||||
## explicit; go 1.16
|
|
||||||
github.com/tidwall/buntdb
|
github.com/tidwall/buntdb
|
||||||
# github.com/tidwall/gjson v1.12.1
|
# github.com/tidwall/gjson v1.14.3
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/tidwall/gjson
|
github.com/tidwall/gjson
|
||||||
# github.com/tidwall/grect v0.1.4
|
# github.com/tidwall/grect v0.1.4
|
||||||
|
Loading…
Reference in New Issue
Block a user