This commit is contained in:
re 2022-12-12 15:30:42 +03:00
parent 71088bcabf
commit 9352908342
4 changed files with 70 additions and 45 deletions

View File

@ -3,8 +3,8 @@
src="logo.png" src="logo.png"
width="307" height="150" border="0" alt="BuntDB"> width="307" height="150" border="0" alt="BuntDB">
<br> <br>
<a href="https://godoc.org/github.com/tidwall/buntdb"><img src="https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square" alt="Godoc"></a> <a href="https://godoc.org/git.internal/re/buntdb"><img src="https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square" alt="Godoc"></a>
<a href="https://github.com/tidwall/buntdb/blob/master/LICENSE"><img src="https://img.shields.io/github/license/tidwall/buntdb.svg?style=flat-square" alt="LICENSE"></a> <a href="https://git.internal/re/buntdb/blob/master/LICENSE"><img src="https://img.shields.io/github/license/tidwall/buntdb.svg?style=flat-square" alt="LICENSE"></a>
</p> </p>
BuntDB is a low-level, in-memory, key/value store in pure Go. BuntDB is a low-level, in-memory, key/value store in pure Go.
@ -17,7 +17,7 @@ Features
======== ========
- In-memory database for [fast reads and writes](#performance) - In-memory database for [fast reads and writes](#performance)
- Embeddable with a [simple API](https://godoc.org/github.com/tidwall/buntdb) - Embeddable with a [simple API](https://godoc.org/git.internal/re/buntdb)
- [Spatial indexing](#spatial-indexes) for up to 20 dimensions; Useful for Geospatial data - [Spatial indexing](#spatial-indexes) for up to 20 dimensions; Useful for Geospatial data
- Index fields inside [JSON](#json-indexes) documents - Index fields inside [JSON](#json-indexes) documents
- [Collate i18n Indexes](#collate-i18n-indexes) using the optional [collate package](https://github.com/tidwall/collate) - [Collate i18n Indexes](#collate-i18n-indexes) using the optional [collate package](https://github.com/tidwall/collate)
@ -38,7 +38,7 @@ Getting Started
To start using BuntDB, install Go and run `go get`: To start using BuntDB, install Go and run `go get`:
```sh ```sh
$ go get -u github.com/tidwall/buntdb $ go get -u git.internal/re/buntdb
``` ```
This will retrieve the library. This will retrieve the library.
@ -55,7 +55,7 @@ package main
import ( import (
"log" "log"
"github.com/tidwall/buntdb" "git.internal/re/buntdb"
) )
func main() { func main() {
@ -143,7 +143,7 @@ err := db.View(func(tx *buntdb.Tx) error {
}) })
``` ```
There is also `AscendGreaterOrEqual`, `AscendLessThan`, `AscendRange`, `AscendEqual`, `Descend`, `DescendLessOrEqual`, `DescendGreaterThan`, `DescendRange`, and `DescendEqual`. Please see the [documentation](https://godoc.org/github.com/tidwall/buntdb) for more information on these functions. There is also `AscendGreaterOrEqual`, `AscendLessThan`, `AscendRange`, `AscendEqual`, `Descend`, `DescendLessOrEqual`, `DescendGreaterThan`, `DescendRange`, and `DescendEqual`. Please see the [documentation](https://godoc.org/git.internal/re/buntdb) for more information on these functions.
## Custom Indexes ## Custom Indexes
@ -361,7 +361,7 @@ package main
import ( import (
"fmt" "fmt"
"github.com/tidwall/buntdb" "git.internal/re/buntdb"
) )
func main() { func main() {
@ -594,7 +594,7 @@ go test --bench=.
### BuntDB-Benchmark ### BuntDB-Benchmark
There's a [custom utility](https://github.com/tidwall/buntdb-benchmark) that was created specifically for benchmarking BuntDB. There's a [custom utility](https://git.internal/re/buntdb-benchmark) that was created specifically for benchmarking BuntDB.
*These are the results from running the benchmarks on a MacBook Pro 15" 2.8 GHz Intel Core i7:* *These are the results from running the benchmarks on a MacBook Pro 15" 2.8 GHz Intel Core i7:*
@ -620,7 +620,7 @@ SPATIAL_INTERSECTS_800: 159673.91 operations per second
To install this utility: To install this utility:
``` ```
go get github.com/tidwall/buntdb-benchmark go get git.internal/re/buntdb-benchmark
``` ```

View File

@ -16,11 +16,11 @@ import (
"sync" "sync"
"time" "time"
"git.internal/re/rtred"
"github.com/tidwall/btree" "github.com/tidwall/btree"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
"github.com/tidwall/grect" "github.com/tidwall/grect"
"github.com/tidwall/match" "github.com/tidwall/match"
"github.com/tidwall/rtred"
) )
var ( var (
@ -155,7 +155,7 @@ func Open(path string) (*DB, error) {
if db.persist { if db.persist {
var err error var err error
// hardcoding 0666 as the default mode. // hardcoding 0666 as the default mode.
db.file, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666) db.file, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o666)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -336,7 +336,8 @@ func (idx *index) rebuild() {
// There are some default less function that can be used such as // There are some default less function that can be used such as
// IndexString, IndexBinary, etc. // IndexString, IndexBinary, etc.
func (db *DB) CreateIndex(name, pattern string, func (db *DB) CreateIndex(name, pattern string,
less ...func(a, b string) bool) error { less ...func(a, b string) bool,
) error {
return db.Update(func(tx *Tx) error { return db.Update(func(tx *Tx) error {
return tx.CreateIndex(name, pattern, less...) return tx.CreateIndex(name, pattern, less...)
}) })
@ -347,7 +348,8 @@ func (db *DB) CreateIndex(name, pattern string,
// Ascend* and Descend* methods. // Ascend* and Descend* methods.
// If a previous index with the same name exists, that index will be deleted. // If a previous index with the same name exists, that index will be deleted.
func (db *DB) ReplaceIndex(name, pattern string, func (db *DB) ReplaceIndex(name, pattern string,
less ...func(a, b string) bool) error { less ...func(a, b string) bool,
) error {
return db.Update(func(tx *Tx) error { return db.Update(func(tx *Tx) error {
err := tx.CreateIndex(name, pattern, less...) err := tx.CreateIndex(name, pattern, less...)
if err != nil { if err != nil {
@ -379,7 +381,8 @@ func (db *DB) ReplaceIndex(name, pattern string,
// The IndexRect is a default function that can be used for the rect // The IndexRect is a default function that can be used for the rect
// parameter. // parameter.
func (db *DB) CreateSpatialIndex(name, pattern string, func (db *DB) CreateSpatialIndex(name, pattern string,
rect func(item string) (min, max []float64)) error { rect func(item string) (min, max []float64),
) error {
return db.Update(func(tx *Tx) error { return db.Update(func(tx *Tx) error {
return tx.CreateSpatialIndex(name, pattern, rect) return tx.CreateSpatialIndex(name, pattern, rect)
}) })
@ -390,7 +393,8 @@ func (db *DB) CreateSpatialIndex(name, pattern string,
// Intersects method. // Intersects method.
// If a previous index with the same name exists, that index will be deleted. // If a previous index with the same name exists, that index will be deleted.
func (db *DB) ReplaceSpatialIndex(name, pattern string, func (db *DB) ReplaceSpatialIndex(name, pattern string,
rect func(item string) (min, max []float64)) error { rect func(item string) (min, max []float64),
) error {
return db.Update(func(tx *Tx) error { return db.Update(func(tx *Tx) error {
err := tx.CreateSpatialIndex(name, pattern, rect) err := tx.CreateSpatialIndex(name, pattern, rect)
if err != nil { if err != nil {
@ -417,7 +421,7 @@ func (db *DB) DropIndex(name string) error {
// Indexes returns a list of index names. // Indexes returns a list of index names.
func (db *DB) Indexes() ([]string, error) { func (db *DB) Indexes() ([]string, error) {
var names []string var names []string
var err = db.View(func(tx *Tx) error { err := db.View(func(tx *Tx) error {
var err error var err error
names, err = tx.Indexes() names, err = tx.Indexes()
return err return err
@ -754,7 +758,7 @@ func (db *DB) Shrink() error {
if err := os.Rename(tmpname, fname); err != nil { if err := os.Rename(tmpname, fname); err != nil {
panicErr(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, 0o666)
if err != nil { if err != nil {
panicErr(err) panicErr(err)
} }
@ -1455,7 +1459,8 @@ func (tx *Tx) GetLess(index string) (func(a, b string) bool, error) {
// Returns ErrNotFound if the index is not found or there is no rect // Returns ErrNotFound if the index is not found or there is no rect
// function bound to the index // function bound to the index
func (tx *Tx) GetRect(index string) (func(s string) (min, max []float64), func (tx *Tx) GetRect(index string) (func(s string) (min, max []float64),
error) { error,
) {
if tx.db == nil { if tx.db == nil {
return nil, ErrTxClosed return nil, ErrTxClosed
} }
@ -1478,7 +1483,8 @@ func (tx *Tx) GetRect(index string) (func(s string) (min, max []float64),
// Only a writable transaction can be used with this operation. // Only a writable transaction can be used with this operation.
// This operation is not allowed during iterations such as Ascend* & Descend*. // This operation is not allowed during iterations such as Ascend* & Descend*.
func (tx *Tx) Set(key, value string, opts *SetOptions) (previousValue string, func (tx *Tx) Set(key, value string, opts *SetOptions) (previousValue string,
replaced bool, err error) { replaced bool, err error,
) {
if tx.db == nil { if tx.db == nil {
return "", false, ErrTxClosed return "", false, ErrTxClosed
} else if !tx.writable { } else if !tx.writable {
@ -1615,7 +1621,8 @@ func (tx *Tx) TTL(key string) (time.Duration, error) {
// descending order, these will be lessThan, greaterThan. // descending order, these will be lessThan, greaterThan.
// An error will be returned if the tx is closed or the index is not found. // An error will be returned if the tx is closed or the index is not found.
func (tx *Tx) scan(desc, gt, lt bool, index, start, stop string, func (tx *Tx) scan(desc, gt, lt bool, index, start, stop string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
if tx.db == nil { if tx.db == nil {
return ErrTxClosed return ErrTxClosed
} }
@ -1698,7 +1705,8 @@ func Match(key, pattern string) bool {
// AscendKeys allows for iterating through keys based on the specified pattern. // AscendKeys allows for iterating through keys based on the specified pattern.
func (tx *Tx) AscendKeys(pattern string, func (tx *Tx) AscendKeys(pattern string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
if pattern == "" { if pattern == "" {
return nil return nil
} }
@ -1731,7 +1739,8 @@ func (tx *Tx) AscendKeys(pattern string,
// DescendKeys allows for iterating through keys based on the specified pattern. // DescendKeys allows for iterating through keys based on the specified pattern.
func (tx *Tx) DescendKeys(pattern string, func (tx *Tx) DescendKeys(pattern string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
if pattern == "" { if pattern == "" {
return nil return nil
} }
@ -1769,7 +1778,8 @@ func (tx *Tx) DescendKeys(pattern string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) Ascend(index string, func (tx *Tx) Ascend(index string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan(false, false, false, index, "", "", iterator) return tx.scan(false, false, false, index, "", "", iterator)
} }
@ -1780,7 +1790,8 @@ func (tx *Tx) Ascend(index string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) AscendGreaterOrEqual(index, pivot string, func (tx *Tx) AscendGreaterOrEqual(index, pivot string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan(false, true, false, index, pivot, "", iterator) return tx.scan(false, true, false, index, pivot, "", iterator)
} }
@ -1791,7 +1802,8 @@ func (tx *Tx) AscendGreaterOrEqual(index, pivot string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) AscendLessThan(index, pivot string, func (tx *Tx) AscendLessThan(index, pivot string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan(false, false, true, index, pivot, "", iterator) return tx.scan(false, false, true, index, pivot, "", iterator)
} }
@ -1802,7 +1814,8 @@ func (tx *Tx) AscendLessThan(index, pivot string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) AscendRange(index, greaterOrEqual, lessThan string, func (tx *Tx) AscendRange(index, greaterOrEqual, lessThan string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan( return tx.scan(
false, true, true, index, greaterOrEqual, lessThan, iterator, false, true, true, index, greaterOrEqual, lessThan, iterator,
) )
@ -1815,7 +1828,8 @@ func (tx *Tx) AscendRange(index, greaterOrEqual, lessThan string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) Descend(index string, func (tx *Tx) Descend(index string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan(true, false, false, index, "", "", iterator) return tx.scan(true, false, false, index, "", "", iterator)
} }
@ -1826,7 +1840,8 @@ func (tx *Tx) Descend(index string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) DescendGreaterThan(index, pivot string, func (tx *Tx) DescendGreaterThan(index, pivot string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan(true, true, false, index, pivot, "", iterator) return tx.scan(true, true, false, index, pivot, "", iterator)
} }
@ -1837,7 +1852,8 @@ func (tx *Tx) DescendGreaterThan(index, pivot string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) DescendLessOrEqual(index, pivot string, func (tx *Tx) DescendLessOrEqual(index, pivot string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan(true, false, true, index, pivot, "", iterator) return tx.scan(true, false, true, index, pivot, "", iterator)
} }
@ -1848,7 +1864,8 @@ func (tx *Tx) DescendLessOrEqual(index, pivot string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) DescendRange(index, lessOrEqual, greaterThan string, func (tx *Tx) DescendRange(index, lessOrEqual, greaterThan string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
return tx.scan( return tx.scan(
true, true, true, index, lessOrEqual, greaterThan, iterator, true, true, true, index, lessOrEqual, greaterThan, iterator,
) )
@ -1861,7 +1878,8 @@ func (tx *Tx) DescendRange(index, lessOrEqual, greaterThan string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) AscendEqual(index, pivot string, func (tx *Tx) AscendEqual(index, pivot string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
var err error var err error
var less func(a, b string) bool var less func(a, b string) bool
if index != "" { if index != "" {
@ -1889,7 +1907,8 @@ func (tx *Tx) AscendEqual(index, pivot string,
// When an index is not provided, the results will be ordered by the item key. // When an index is not provided, the results will be ordered by the item key.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) DescendEqual(index, pivot string, func (tx *Tx) DescendEqual(index, pivot string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
var err error var err error
var less func(a, b string) bool var less func(a, b string) bool
if index != "" { if index != "" {
@ -1929,7 +1948,8 @@ func (r *rect) Rect(ctx interface{}) (min, max []float64) {
// The dist param is the distance of the bounding boxes. In the case of // The dist param is the distance of the bounding boxes. In the case of
// simple 2D points, it's the distance of the two 2D points squared. // simple 2D points, it's the distance of the two 2D points squared.
func (tx *Tx) Nearby(index, bounds string, func (tx *Tx) Nearby(index, bounds string,
iterator func(key, value string, dist float64) bool) error { iterator func(key, value string, dist float64) bool,
) error {
if tx.db == nil { if tx.db == nil {
return ErrTxClosed return ErrTxClosed
} }
@ -1967,7 +1987,8 @@ func (tx *Tx) Nearby(index, bounds string,
// same bounds function that was passed to the CreateSpatialIndex() function. // same bounds function that was passed to the CreateSpatialIndex() function.
// An invalid index will return an error. // An invalid index will return an error.
func (tx *Tx) Intersects(index, bounds string, func (tx *Tx) Intersects(index, bounds string,
iterator func(key, value string) bool) error { iterator func(key, value string) bool,
) error {
if tx.db == nil { if tx.db == nil {
return ErrTxClosed return ErrTxClosed
} }
@ -2030,7 +2051,8 @@ type IndexOptions struct {
// There are some default less function that can be used such as // There are some default less function that can be used such as
// IndexString, IndexBinary, etc. // IndexString, IndexBinary, etc.
func (tx *Tx) CreateIndex(name, pattern string, func (tx *Tx) CreateIndex(name, pattern string,
less ...func(a, b string) bool) error { less ...func(a, b string) bool,
) error {
return tx.createIndex(name, pattern, less, nil, nil) return tx.createIndex(name, pattern, less, nil, nil)
} }
@ -2038,7 +2060,8 @@ func (tx *Tx) CreateIndex(name, pattern string,
// for additional options. // for additional options.
func (tx *Tx) CreateIndexOptions(name, pattern string, func (tx *Tx) CreateIndexOptions(name, pattern string,
opts *IndexOptions, opts *IndexOptions,
less ...func(a, b string) bool) error { less ...func(a, b string) bool,
) error {
return tx.createIndex(name, pattern, less, nil, opts) return tx.createIndex(name, pattern, less, nil, opts)
} }
@ -2057,7 +2080,8 @@ func (tx *Tx) CreateIndexOptions(name, pattern string,
// The IndexRect is a default function that can be used for the rect // The IndexRect is a default function that can be used for the rect
// parameter. // parameter.
func (tx *Tx) CreateSpatialIndex(name, pattern string, func (tx *Tx) CreateSpatialIndex(name, pattern string,
rect func(item string) (min, max []float64)) error { rect func(item string) (min, max []float64),
) error {
return tx.createIndex(name, pattern, nil, rect, nil) return tx.createIndex(name, pattern, nil, rect, nil)
} }
@ -2065,7 +2089,8 @@ func (tx *Tx) CreateSpatialIndex(name, pattern string,
// it allows for additional options. // it allows for additional options.
func (tx *Tx) CreateSpatialIndexOptions(name, pattern string, func (tx *Tx) CreateSpatialIndexOptions(name, pattern string,
opts *IndexOptions, opts *IndexOptions,
rect func(item string) (min, max []float64)) error { rect func(item string) (min, max []float64),
) error {
return tx.createIndex(name, pattern, nil, rect, nil) return tx.createIndex(name, pattern, nil, rect, nil)
} }

6
go.mod
View File

@ -1,18 +1,18 @@
module github.com/tidwall/buntdb module git.internal/re/buntdb
go 1.18 go 1.18
require ( require (
git.internal/re/rtred v0.1.3
github.com/tidwall/assert v0.1.0 github.com/tidwall/assert v0.1.0
github.com/tidwall/btree v1.4.2 github.com/tidwall/btree v1.4.2
github.com/tidwall/gjson v1.14.3 github.com/tidwall/gjson v1.14.3
github.com/tidwall/grect v0.1.4 github.com/tidwall/grect v0.1.4
github.com/tidwall/lotsa v1.0.2 github.com/tidwall/lotsa v1.0.2
github.com/tidwall/match v1.1.1 github.com/tidwall/match v1.1.1
github.com/tidwall/rtred v0.1.2
) )
require ( require (
git.internal/re/tinyqueue v0.1.3 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect
) )

8
go.sum
View File

@ -1,3 +1,7 @@
git.internal/re/rtred v0.1.3 h1:PR21v+kqXJMJrLe18CccJdPJk1xR26SRmKrmeWmVFGQ=
git.internal/re/rtred v0.1.3/go.mod h1:zV1xdKGrD0vDOtMmwL4TB3hF64becmB6SdE3pqeaVjI=
git.internal/re/tinyqueue v0.1.3 h1:4EKqZhzHLgoE7qp3+Jy4fLTJ+VXYdnREYiErcW6/Tds=
git.internal/re/tinyqueue v0.1.3/go.mod h1:xwfmZ1Jo/JI0eXLc78eGZv+kEtJipgyui683yVTh7fc=
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g= github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g=
@ -13,7 +17,3 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=