added disk quota

This commit is contained in:
ston1th 2022-03-17 00:47:45 +01:00
commit 9cc2b04342
26 changed files with 1376 additions and 61 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/cachefs /cachefs
debug.txt

View file

@ -31,7 +31,7 @@ clean:
gofmt: gofmt:
$(CC) fmt ./... $(CC) fmt ./...
codeqa: govet misspell staticcheck test codeqa: govet misspell test
govet: govet:
$(CC) vet ./... $(CC) vet ./...

View file

@ -1,4 +1,2 @@
* add disk quota * fix deadlock
* preload: dont load file if quota exceeded * maybe dirEmpty?
* add LRU cache
* clear cache if file sizes differ and > 0

View file

@ -27,10 +27,13 @@ var (
metadata string metadata string
listenHttp string listenHttp string
listenWebdav string listenWebdav string
quota int64
log logr.Logger log logr.Logger
) )
const gib = 1024 * 1024 * 1024
func main() { func main() {
klog.InitFlags(nil) klog.InitFlags(nil)
flag.StringVar(&src, "src", "", "root directory (NFS share)") flag.StringVar(&src, "src", "", "root directory (NFS share)")
@ -38,12 +41,13 @@ func main() {
flag.StringVar(&metadata, "data", "", "path to metadata file") flag.StringVar(&metadata, "data", "", "path to metadata file")
flag.StringVar(&listenHttp, "listen", "127.0.0.1:8080", "listen addr:port") flag.StringVar(&listenHttp, "listen", "127.0.0.1:8080", "listen addr:port")
flag.StringVar(&listenWebdav, "webdav", "", "listen addr:port for webdav") flag.StringVar(&listenWebdav, "webdav", "", "listen addr:port for webdav")
flag.Int64Var(&quota, "quota", 1, "max disk usage quota in GiB")
flag.Parse() flag.Parse()
log = klogr.New().WithName("main") log = klogr.New().WithName("main")
log.Info("starting cachefs", "version", version) log.Info("starting cachefs", "version", version)
filesystem, err := fs.NewFS(src, dst, metadata, klogr.New().WithName("fs")) filesystem, err := fs.NewFS(quota*gib, src, dst, metadata, klogr.New().WithName("fs"))
if err != nil { if err != nil {
klog.Fatalf("init failed: %s", err) klog.Fatalf("init failed: %s", err)
} }

3
go.mod
View file

@ -1,9 +1,10 @@
module cachefs module cachefs
go 1.17 go 1.18
require ( require (
github.com/go-logr/logr v1.2.2 github.com/go-logr/logr v1.2.2
golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7
golang.org/x/net v0.0.0-20220225172249-27dd8689420f golang.org/x/net v0.0.0-20220225172249-27dd8689420f
k8s.io/klog/v2 v2.40.1 k8s.io/klog/v2 v2.40.1
) )

7
go.sum
View file

@ -1,12 +1,9 @@
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7 h1:jynE66seADJbyWMUdeOyVTvPtBZt7L6LJHupGwxPZRM=
golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4= k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4=
k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=

View file

@ -2,15 +2,15 @@
package fs package fs
import "sort" import (
"golang.org/x/exp/slices"
)
type Chunk [2]int64 type Chunk [2]int64
type Chunks []Chunk type Chunks []Chunk
func (cs Chunks) Len() int { return len(cs) } func (Chunks) Less(i, j Chunk) bool { return i[0] < j[0] }
func (cs Chunks) Less(i, j int) bool { return cs[i][0] < cs[j][0] }
func (cs Chunks) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
func (cs Chunks) Exists(off int64, n int, size int64) bool { func (cs Chunks) Exists(off int64, n int, size int64) bool {
end := off + int64(n) end := off + int64(n)
@ -42,7 +42,7 @@ func (cs *Chunks) merge() {
if len(c) < 2 { if len(c) < 2 {
return return
} }
sort.Sort(*cs) slices.SortFunc(c, c.Less)
for i := 0; i < len(c); i++ { for i := 0; i < len(c); i++ {
if i+1 == len(c) { if i+1 == len(c) {
break break
@ -51,7 +51,7 @@ func (cs *Chunks) merge() {
if c[i+1][1] > c[i][1] { if c[i+1][1] > c[i][1] {
c[i][1] = c[i+1][1] c[i][1] = c[i+1][1]
} }
c = append(c[:i+1], c[i+2:]...) c = slices.Delete(c, i+1, i+2)
i-- i--
} }
} }

View file

@ -18,6 +18,7 @@ import (
"time" "time"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"golang.org/x/exp/maps"
) )
type FS struct { type FS struct {
@ -29,12 +30,13 @@ type FS struct {
mdf *os.File mdf *os.File
jenc *json.Encoder jenc *json.Encoder
dc *DirCache dc *DirCache
mm map[string]*Metadata q *Quota
prem map[string]func() md MetadataMap
pm map[string]func()
cancel func() cancel func()
} }
func NewFS(src, dst, metadata string, log logr.Logger) (fs *FS, err error) { func NewFS(max int64, src, dst, metadata string, log logr.Logger) (fs *FS, err error) {
if !filepath.IsAbs(src) { if !filepath.IsAbs(src) {
return nil, errors.New("src path is not absolute") return nil, errors.New("src path is not absolute")
} }
@ -58,15 +60,20 @@ func NewFS(src, dst, metadata string, log logr.Logger) (fs *FS, err error) {
dst: dst, dst: dst,
mdf: mdf, mdf: mdf,
dc: NewDirCache(), dc: NewDirCache(),
mm: make(map[string]*Metadata), md: make(MetadataMap),
prem: make(map[string]func()), pm: make(map[string]func()),
jenc: json.NewEncoder(mdf), jenc: json.NewEncoder(mdf),
} }
err = json.NewDecoder(mdf).Decode(&fs.mm) fs.q, err = NewQuota(max, fs, fs.log.WithName("quota"))
if err != nil {
return
}
err = json.NewDecoder(mdf).Decode(&fs.md)
if err == io.EOF { if err == io.EOF {
err = nil err = nil
} }
fs.initMetadata() fs.initMetadata()
fs.q.Init()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
fs.cancel = cancel fs.cancel = cancel
go fs.flusher(ctx) go fs.flusher(ctx)
@ -75,26 +82,25 @@ func NewFS(src, dst, metadata string, log logr.Logger) (fs *FS, err error) {
func (fs *FS) initMetadata() { func (fs *FS) initMetadata() {
log := fs.log log := fs.log
for k, v := range fs.mm { maps.DeleteFunc(fs.md, func(k string, v *Metadata) bool {
_, err := fs.statDst(k) _, err := fs.statDst(k)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
log.V(2).Info("removing not existing file from metadata", "file", k) log.V(2).Info("removing not existing file from metadata", "file", k)
delete(fs.mm, k) return true
continue
} }
if len(v.Chunks) == 0 { if len(v.Chunks) == 0 {
log.V(2).Info("removing empty file", "file", k) log.V(2).Info("removing empty file", "file", k)
err := fs.RemoveDst(k) err := fs.RemoveDst(k)
if err != nil { if err != nil {
log.Error(err, "error removing empty file", "file", k) log.Error(err, "error removing empty file", "file", k)
continue return false
} }
delete(fs.mm, k) return true
continue
} }
v.fs = fs v.fs = fs
v.name = k v.name = k
} return false
})
} }
func (fs *FS) Stat(name string) (fi stdfs.FileInfo, err error) { func (fs *FS) Stat(name string) (fi stdfs.FileInfo, err error) {
@ -126,7 +132,7 @@ func (fs *FS) statDst(name string) (fi stdfs.FileInfo, err error) {
} }
func (fs *FS) CancelPreload(name string) { func (fs *FS) CancelPreload(name string) {
if cancel, ok := fs.prem[name]; ok { if cancel, ok := fs.pm[name]; ok {
cancel() cancel()
} }
} }
@ -146,9 +152,9 @@ func (fs *FS) Preload(name string) {
return return
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
fs.prem[name] = cancel fs.pm[name] = cancel
unlock := func() { unlock := func() {
delete(fs.prem, name) delete(fs.pm, name)
f.md.UnlockPreload() f.md.UnlockPreload()
} }
go f.Preload(ctx, unlock) go f.Preload(ctx, unlock)
@ -199,15 +205,17 @@ func (fs *FS) Open(name string) (f http.File, err error) {
offline = true offline = true
log.V(2).Info("dir offline mode", "path", dp) log.V(2).Info("dir offline mode", "path", dp)
} }
return &Dir{f: sf, dc: fs.dc}, err f = &Dir{f: sf, dc: fs.dc}
return
} }
md := fs.metadata(name, sfi.Size()) md := fs.metadata(name, sfi.Size())
return &File{ f = &File{
log: log, log: log,
f: sf, f: sf,
md: md, md: md,
offline: offline, offline: offline,
}, nil }
return
} }
func (fs *FS) openCacheFile(name string, size int64) (df *os.File, err error) { func (fs *FS) openCacheFile(name string, size int64) (df *os.File, err error) {
@ -248,19 +256,29 @@ func (fs *FS) openCacheFile(name string, size int64) (df *os.File, err error) {
func (fs *FS) metadata(name string, size int64) (md *Metadata) { func (fs *FS) metadata(name string, size int64) (md *Metadata) {
fs.mu.Lock() fs.mu.Lock()
fs.log.Info("fs.mu.Lock()")
defer fs.mu.Unlock() defer fs.mu.Unlock()
if md, ok := fs.mm[name]; ok { defer fs.log.Info("fs.mu.Unlock()")
if md, ok := fs.md[name]; ok {
if size > 0 && md.Size != size {
log := fs.log.WithValues("file", name)
log.V(2).Info("source file changed: deleting cache file", "src", size, "dst", md.Size)
err := md.Delete()
if err != nil {
log.Error(err, "error deleting cache file")
}
}
return md return md
} }
md = &Metadata{Size: size, fs: fs, name: name} md = &Metadata{Size: size, fs: fs, name: name}
fs.mm[name] = md fs.md[name] = md
return return
} }
func (fs *FS) CacheStatus(name string) int { func (fs *FS) CacheStatus(name string) int {
fs.mu.RLock() fs.mu.RLock()
defer fs.mu.RUnlock() defer fs.mu.RUnlock()
if md, ok := fs.mm[name]; ok { if md, ok := fs.md[name]; ok {
return int(math.Round(float64(md.Chunks.Size()) / float64(md.Size) * 100)) return int(math.Round(float64(md.Chunks.Size()) / float64(md.Size) * 100))
} }
return -1 return -1
@ -281,12 +299,11 @@ func (fs *FS) flushMetadata() {
log := fs.log log := fs.log
fs.mu.Lock() fs.mu.Lock()
defer fs.mu.Unlock() defer fs.mu.Unlock()
mm := make(map[string]*Metadata) log.V(2).Info("flushing metadata to disk")
for k, v := range fs.mm { md := maps.Clone(fs.md)
if v.f != nil || len(v.Chunks) > 0 { maps.DeleteFunc(md, func(_ string, v *Metadata) bool {
mm[k] = v return len(v.Chunks) == 0
} })
}
_, err := fs.mdf.Seek(0, io.SeekStart) _, err := fs.mdf.Seek(0, io.SeekStart)
if err != nil { if err != nil {
log.Error(err, "failure seeking metadata file") log.Error(err, "failure seeking metadata file")
@ -297,7 +314,7 @@ func (fs *FS) flushMetadata() {
log.Error(err, "failure truncating metadata file") log.Error(err, "failure truncating metadata file")
return return
} }
err = fs.jenc.Encode(mm) err = fs.jenc.Encode(md)
if err != nil { if err != nil {
log.Error(err, "failure flushing metadata file") log.Error(err, "failure flushing metadata file")
} }
@ -305,7 +322,10 @@ func (fs *FS) flushMetadata() {
func (fs *FS) Close() { func (fs *FS) Close() {
fs.cancel() fs.cancel()
for _, md := range fs.mm { for _, cancel := range fs.pm {
cancel()
}
for _, md := range fs.md {
md.f.Sync() md.f.Sync()
md.f.Close() md.f.Close()
} }

View file

@ -5,14 +5,23 @@ package fs
import ( import (
"os" "os"
"sync" "sync"
"sync/atomic"
"time"
) )
func now() int64 {
return time.Now().UTC().Unix()
}
type MetadataMap map[string]*Metadata
type Metadata struct { type Metadata struct {
mu sync.RWMutex `json:"-"` mu sync.RWMutex `json:"-"`
fs *FS `json:"-"` fs *FS `json:"-"`
f *os.File `json:"-"` f *os.File `json:"-"`
name string `json:"-"` name string `json:"-"`
Size int64 `json:"s"` Size int64 `json:"s"`
Atime int64 `json:"a"`
Chunks Chunks `json:"c"` Chunks Chunks `json:"c"`
preload bool `json:"-"` preload bool `json:"-"`
} }
@ -23,6 +32,7 @@ func (md *Metadata) Delete() error {
md.Chunks = Chunks{} md.Chunks = Chunks{}
md.f.Close() md.f.Close()
md.f = nil md.f = nil
atomic.StoreInt64(&md.Atime, now())
return md.fs.RemoveDst(md.name) return md.fs.RemoveDst(md.name)
} }
@ -45,6 +55,7 @@ func (md *Metadata) ReadAt(p []byte, pos int64) (int, error) {
return 0, err return 0, err
} }
} }
atomic.StoreInt64(&md.Atime, now())
return md.f.ReadAt(p, pos) return md.f.ReadAt(p, pos)
} }
@ -55,6 +66,8 @@ func (md *Metadata) WriteAt(data []byte, pos int64) (int, error) {
return 0, err return 0, err
} }
} }
atomic.StoreInt64(&md.Atime, now())
md.fs.q.Add(len(data))
return md.f.WriteAt(data, pos) return md.f.WriteAt(data, pos)
} }

70
pkg/fs/quota.go Normal file
View file

@ -0,0 +1,70 @@
// Copyright (C) 2022 Marius Schellenberger
package fs
import (
"errors"
"math"
"sync/atomic"
"github.com/go-logr/logr"
)
var InvalidMaxQuota = errors.New("invalid max quota")
type Quota struct {
log logr.Logger
fs *FS
max int64
cur int64
}
func NewQuota(max int64, fs *FS, log logr.Logger) (q *Quota, err error) {
if max <= 0 {
err = InvalidMaxQuota
return
}
q = &Quota{log: log, fs: fs, max: max}
return
}
func (q *Quota) Init() {
for _, v := range q.fs.md {
q.cur += v.Chunks.Size()
}
q.log.Info("quota usage", "current", q.cur, "max", q.max)
}
func (q *Quota) cleanup() {
if !q.fs.mu.TryLock() {
return
}
q.log.Info("!q.fs.mu.TryLock()")
q.log.Info("quota usage", "current", q.cur, "max", q.max)
defer q.fs.mu.Unlock()
defer q.log.Info("q.fs.mu.Unlock()")
atime := int64(math.MaxInt64)
var m *Metadata
for _, v := range q.fs.md {
if v.Atime != 0 && v.f != nil && v.Atime < atime {
atime = v.Atime
m = v
}
}
if m != nil {
log := q.log.WithValues("file", m.name)
log.Info("deleting oldest file")
err := m.Delete()
if err != nil {
q.log.Error(err, "error deleting oldest file")
return
}
atomic.AddInt64(&q.cur, -m.Size)
}
}
func (q *Quota) Add(n int) {
if q.max < atomic.AddInt64(&q.cur, int64(n)) {
q.cleanup()
}
}

View file

@ -10,6 +10,8 @@ import (
"strings" "strings"
"cachefs/pkg/fs" "cachefs/pkg/fs"
"golang.org/x/exp/slices"
) )
type statusInterceptor struct { type statusInterceptor struct {
@ -51,9 +53,7 @@ type file struct {
type files []file type files []file
func (f files) Len() int { return len(f) } func (files) Less(i, j file) bool { return i.Name < j.Name }
func (f files) Less(i, j int) bool { return f[i].Name < f[j].Name }
func (f files) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
type responseInterceptor struct { type responseInterceptor struct {
buf bytes.Buffer buf bytes.Buffer
@ -98,7 +98,7 @@ func (r *responseInterceptor) GetPaths(path string, fs *fs.FS) (dir dirContents,
} }
} }
sort.Strings(dir.Dirs) sort.Strings(dir.Dirs)
sort.Sort(dir.Files) slices.SortFunc(dir.Files, dir.Files.Less)
return return
} }

View file

@ -7,7 +7,9 @@ import (
"io" "io"
stdfs "io/fs" stdfs "io/fs"
"net/http" "net/http"
"os"
"path" "path"
"runtime"
"strings" "strings"
"cachefs/pkg/fs" "cachefs/pkg/fs"
@ -15,6 +17,21 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
) )
func PrintStack() {
os.Stderr.Write(Stack())
}
func Stack() []byte {
buf := make([]byte, 1024)
for {
n := runtime.Stack(buf, true)
if n < len(buf) {
return buf[:n]
}
buf = make([]byte, 2*len(buf))
}
}
type FileServer struct { type FileServer struct {
log logr.Logger log logr.Logger
fs *fs.FS fs *fs.FS
@ -51,13 +68,17 @@ func (fs *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.URL.Path = upath r.URL.Path = upath
} }
p := path.Clean(upath) p := path.Clean(upath)
option := r.FormValue("o")
if option == "debug" {
PrintStack()
}
d, _, err := fs.fs.StatWithOffline(p) d, _, err := fs.fs.StatWithOffline(p)
if err != nil { if err != nil {
msg, code := toHTTPError(err) msg, code := toHTTPError(err)
http.Error(w, msg, code) http.Error(w, msg, code)
return return
} }
option := r.FormValue("o") //option := r.FormValue("o")
if option == "v" { if option == "v" {
err = video.Execute(w, r.URL.Path) err = video.Execute(w, r.URL.Path)
if err != nil { if err != nil {

View file

@ -30,7 +30,7 @@ var (
{{end -}} {{end -}}
{{range $s := .Files -}} {{range $s := .Files -}}
<tr><td><a id="{{$s.Anchor}}" href="{{$s.Name}}">{{$s.Name}}</a></td> <tr><td><a id="{{$s.Anchor}}" href="{{$s.Name}}">{{$s.Name}}</a></td>
<td><a href="{{$s.Name}}?o=v">[v]</a>&nbsp;<a href="{{$s.Name}}?o=n">[n]</a>&nbsp;<a href="{{$s.Name}}?o=p">[p]</a>&nbsp;<a href="{{$s.Name}}?o=s">[s]</a>&nbsp;{{if ge $s.Status 0}}{{$s.Status}}%{{end}}</td></tr> <td><a href="{{$s.Name}}?o=v">[v]</a>&nbsp;<a href="{{$s.Name}}?o=n">[n]</a>&nbsp;<a href="{{$s.Name}}?o=p">[p]</a>&nbsp;<a href="{{$s.Name}}?o=s">[s]</a>&nbsp;{{if ne $s.Status -1}}{{$s.Status}}%{{end}}</td></tr>
{{end -}} {{end -}}
</table> </table>
</article> </article>

View file

@ -9,6 +9,7 @@ import (
"cachefs/pkg/fs" "cachefs/pkg/fs"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"golang.org/x/exp/slices"
"golang.org/x/net/webdav" "golang.org/x/net/webdav"
) )
@ -65,10 +66,5 @@ func skipDavLog(path string) bool {
if i > 0 { if i > 0 {
path = path[i+1:] path = path[i+1:]
} }
for _, v := range skipDavFiles { return slices.Contains(skipDavFiles, path)
if path == v {
return true
}
}
return false
} }

View file

@ -11,7 +11,7 @@ cat <<"EOF">/etc/rc.d/cachefs
#!/bin/sh #!/bin/sh
daemon="/usr/local/sbin/cachefs" daemon="/usr/local/sbin/cachefs"
daemon_flags="-src /mnt/nfs -dst /mnt/cache -data /var/cachefs/md.json -listen :9090 -webdav :9091 -v 2" daemon_flags="-src /mnt/nfs -dst /mnt/cache -data /var/cachefs/md.json -listen :9090 -webdav :9091 -quota 10 -v 2"
daemon_logger="daemon.info" daemon_logger="daemon.info"
daemon_user="_cachefs" daemon_user="_cachefs"

3
vendor/golang.org/x/exp/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

3
vendor/golang.org/x/exp/CONTRIBUTORS generated vendored Normal file
View file

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/exp/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/exp/PATENTS generated vendored Normal file
View file

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

50
vendor/golang.org/x/exp/constraints/constraints.go generated vendored Normal file
View file

@ -0,0 +1,50 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package constraints defines a set of useful constraints to be used
// with type parameters.
package constraints
// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
~float32 | ~float64
}
// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
~complex64 | ~complex128
}
// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
Integer | Float | ~string
}

90
vendor/golang.org/x/exp/maps/maps.go generated vendored Normal file
View file

@ -0,0 +1,90 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package maps defines various functions useful with maps of any type.
package maps
// Keys returns the keys of the map m.
// The keys will be in an indeterminate order.
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
// Values returns the values of the map m.
// The values will be in an indeterminate order.
func Values[M ~map[K]V, K comparable, V any](m M) []V {
r := make([]V, 0, len(m))
for _, v := range m {
r = append(r, v)
}
return r
}
// Equal reports whether two maps contain the same key/value pairs.
// Values are compared using ==.
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || v1 != v2 {
return false
}
}
return true
}
// EqualFunc is like Equal, but compares values using eq.
// Keys are still compared with ==.
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || !eq(v1, v2) {
return false
}
}
return true
}
// Clear removes all entries from m, leaving it empty.
func Clear[M ~map[K]V, K comparable, V any](m M) {
for k := range m {
delete(m, k)
}
}
// Clone returns a copy of m. This is a shallow clone:
// the new keys and values are set using ordinary assignment.
func Clone[M ~map[K]V, K comparable, V any](m M) M {
r := make(M, len(m))
for k, v := range m {
r[k] = v
}
return r
}
// Copy copies all key/value pairs in src adding them to dst.
// When a key in src is already present in dst,
// the value in dst will be overwritten by the value associated
// with the key in src.
func Copy[M ~map[K]V, K comparable, V any](dst, src M) {
for k, v := range src {
dst[k] = v
}
}
// DeleteFunc deletes any key/value pairs from m for which del returns true.
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) {
for k, v := range m {
if del(k, v) {
delete(m, k)
}
}
}

213
vendor/golang.org/x/exp/slices/slices.go generated vendored Normal file
View file

@ -0,0 +1,213 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package slices defines various functions useful with slices of any type.
// Unless otherwise specified, these functions all apply to the elements
// of a slice at index 0 <= i < len(s).
package slices
import "golang.org/x/exp/constraints"
// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in increasing index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[E comparable](s1, s2 []E) bool {
if len(s1) != len(s2) {
return false
}
for i := range s1 {
if s1[i] != s2[i] {
return false
}
}
return true
}
// EqualFunc reports whether two slices are equal using a comparison
// function on each pair of elements. If the lengths are different,
// EqualFunc returns false. Otherwise, the elements are compared in
// increasing index order, and the comparison stops at the first index
// for which eq returns false.
func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
if len(s1) != len(s2) {
return false
}
for i, v1 := range s1 {
v2 := s2[i]
if !eq(v1, v2) {
return false
}
}
return true
}
// Compare compares the elements of s1 and s2.
// The elements are compared sequentially, starting at index 0,
// until one element is not equal to the other.
// The result of comparing the first non-matching elements is returned.
// If both slices are equal until one of them ends, the shorter slice is
// considered less than the longer one.
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
// Comparisons involving floating point NaNs are ignored.
func Compare[E constraints.Ordered](s1, s2 []E) int {
s2len := len(s2)
for i, v1 := range s1 {
if i >= s2len {
return +1
}
v2 := s2[i]
switch {
case v1 < v2:
return -1
case v1 > v2:
return +1
}
}
if len(s1) < s2len {
return -1
}
return 0
}
// CompareFunc is like Compare but uses a comparison function
// on each pair of elements. The elements are compared in increasing
// index order, and the comparisons stop after the first time cmp
// returns non-zero.
// The result is the first non-zero result of cmp; if cmp always
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
// and +1 if len(s1) > len(s2).
func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
s2len := len(s2)
for i, v1 := range s1 {
if i >= s2len {
return +1
}
v2 := s2[i]
if c := cmp(v1, v2); c != 0 {
return c
}
}
if len(s1) < s2len {
return -1
}
return 0
}
// Index returns the index of the first occurrence of v in s,
// or -1 if not present.
func Index[E comparable](s []E, v E) int {
for i, vs := range s {
if v == vs {
return i
}
}
return -1
}
// IndexFunc returns the first index i satisfying f(s[i]),
// or -1 if none do.
func IndexFunc[E any](s []E, f func(E) bool) int {
for i, v := range s {
if f(v) {
return i
}
}
return -1
}
// Contains reports whether v is present in s.
func Contains[E comparable](s []E, v E) bool {
return Index(s, v) >= 0
}
// Insert inserts the values v... into s at index i,
// returning the modified slice.
// In the returned slice r, r[i] == v[0].
// Insert panics if i is out of range.
// This function is O(len(s) + len(v)).
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
tot := len(s) + len(v)
if tot <= cap(s) {
s2 := s[:tot]
copy(s2[i+len(v):], s[i:])
copy(s2[i:], v)
return s2
}
s2 := make(S, tot)
copy(s2, s[:i])
copy(s2[i:], v)
copy(s2[i+len(v):], s[i:])
return s2
}
// Delete removes the elements s[i:j] from s, returning the modified slice.
// Delete panics if s[i:j] is not a valid slice of s.
// Delete modifies the contents of the slice s; it does not create a new slice.
// Delete is O(len(s)-(j-i)), so if many items must be deleted, it is better to
// make a single call deleting them all together than to delete one at a time.
func Delete[S ~[]E, E any](s S, i, j int) S {
return append(s[:i], s[j:]...)
}
// Clone returns a copy of the slice.
// The elements are copied using assignment, so this is a shallow clone.
func Clone[S ~[]E, E any](s S) S {
// Preserve nil in case it matters.
if s == nil {
return nil
}
return append(S([]E{}), s...)
}
// Compact replaces consecutive runs of equal elements with a single copy.
// This is like the uniq command found on Unix.
// Compact modifies the contents of the slice s; it does not create a new slice.
func Compact[S ~[]E, E comparable](s S) S {
if len(s) == 0 {
return s
}
i := 1
last := s[0]
for _, v := range s[1:] {
if v != last {
s[i] = v
i++
last = v
}
}
return s[:i]
}
// CompactFunc is like Compact but uses a comparison function.
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
if len(s) == 0 {
return s
}
i := 1
last := s[0]
for _, v := range s[1:] {
if !eq(v, last) {
s[i] = v
i++
last = v
}
}
return s[:i]
}
// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. Grow may modify elements of the
// slice between the length and the capacity. If n is negative or too large to
// allocate the memory, Grow panics.
func Grow[S ~[]E, E any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
func Clip[S ~[]E, E any](s S) S {
return s[:len(s):len(s)]
}

95
vendor/golang.org/x/exp/slices/sort.go generated vendored Normal file
View file

@ -0,0 +1,95 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package slices
import "golang.org/x/exp/constraints"
// Sort sorts a slice of any ordered type in ascending order.
func Sort[Elem constraints.Ordered](x []Elem) {
n := len(x)
quickSortOrdered(x, 0, n, maxDepth(n))
}
// Sort sorts the slice x in ascending order as determined by the less function.
// This sort is not guaranteed to be stable.
func SortFunc[Elem any](x []Elem, less func(a, b Elem) bool) {
n := len(x)
quickSortLessFunc(x, 0, n, maxDepth(n), less)
}
// SortStable sorts the slice x while keeping the original order of equal
// elements, using less to compare elements.
func SortStableFunc[Elem any](x []Elem, less func(a, b Elem) bool) {
stableLessFunc(x, len(x), less)
}
// IsSorted reports whether x is sorted in ascending order.
func IsSorted[Elem constraints.Ordered](x []Elem) bool {
for i := len(x) - 1; i > 0; i-- {
if x[i] < x[i-1] {
return false
}
}
return true
}
// IsSortedFunc reports whether x is sorted in ascending order, with less as the
// comparison function.
func IsSortedFunc[Elem any](x []Elem, less func(a, b Elem) bool) bool {
for i := len(x) - 1; i > 0; i-- {
if less(x[i], x[i-1]) {
return false
}
}
return true
}
// BinarySearch searches for target in a sorted slice and returns the smallest
// index at which target is found. If the target is not found, the index at
// which it could be inserted into the slice is returned; therefore, if the
// intention is to find target itself a separate check for equality with the
// element at the returned index is required.
func BinarySearch[Elem constraints.Ordered](x []Elem, target Elem) int {
return search(len(x), func(i int) bool { return x[i] >= target })
}
// BinarySearchFunc uses binary search to find and return the smallest index i
// in [0, n) at which ok(i) is true, assuming that on the range [0, n),
// ok(i) == true implies ok(i+1) == true. That is, BinarySearchFunc requires
// that ok is false for some (possibly empty) prefix of the input range [0, n)
// and then true for the (possibly empty) remainder; BinarySearchFunc returns
// the first true index. If there is no such index, BinarySearchFunc returns n.
// (Note that the "not found" return value is not -1 as in, for instance,
// strings.Index.) Search calls ok(i) only for i in the range [0, n).
func BinarySearchFunc[Elem any](x []Elem, ok func(Elem) bool) int {
return search(len(x), func(i int) bool { return ok(x[i]) })
}
// maxDepth returns a threshold at which quicksort should switch
// to heapsort. It returns 2*ceil(lg(n+1)).
func maxDepth(n int) int {
var depth int
for i := n; i > 0; i >>= 1 {
depth++
}
return depth * 2
}
func search(n int, f func(int) bool) int {
// Define f(-1) == false and f(n) == true.
// Invariant: f(i-1) == false, f(j) == true.
i, j := 0, n
for i < j {
h := int(uint(i+j) >> 1) // avoid overflow when computing h
// i ≤ h < j
if !f(h) {
i = h + 1 // preserves f(i-1) == false
} else {
j = h // preserves f(j) == true
}
}
// i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
return i
}

342
vendor/golang.org/x/exp/slices/zsortfunc.go generated vendored Normal file
View file

@ -0,0 +1,342 @@
// Code generated by gen_sort_variants.go; DO NOT EDIT.
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package slices
// insertionSortLessFunc sorts data[a:b] using insertion sort.
func insertionSortLessFunc[Elem any](data []Elem, a, b int, less func(a, b Elem) bool) {
for i := a + 1; i < b; i++ {
for j := i; j > a && less(data[j], data[j-1]); j-- {
data[j], data[j-1] = data[j-1], data[j]
}
}
}
// siftDownLessFunc implements the heap property on data[lo:hi].
// first is an offset into the array where the root of the heap lies.
func siftDownLessFunc[Elem any](data []Elem, lo, hi, first int, less func(a, b Elem) bool) {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && less(data[first+child], data[first+child+1]) {
child++
}
if !less(data[first+root], data[first+child]) {
return
}
data[first+root], data[first+child] = data[first+child], data[first+root]
root = child
}
}
func heapSortLessFunc[Elem any](data []Elem, a, b int, less func(a, b Elem) bool) {
first := a
lo := 0
hi := b - a
// Build heap with greatest element at top.
for i := (hi - 1) / 2; i >= 0; i-- {
siftDownLessFunc(data, i, hi, first, less)
}
// Pop elements, largest first, into end of data.
for i := hi - 1; i >= 0; i-- {
data[first], data[first+i] = data[first+i], data[first]
siftDownLessFunc(data, lo, i, first, less)
}
}
// Quicksort, loosely following Bentley and McIlroy,
// "Engineering a Sort Function" SP&E November 1993.
// medianOfThreeLessFunc moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
func medianOfThreeLessFunc[Elem any](data []Elem, m1, m0, m2 int, less func(a, b Elem) bool) {
// sort 3 elements
if less(data[m1], data[m0]) {
data[m1], data[m0] = data[m0], data[m1]
}
// data[m0] <= data[m1]
if less(data[m2], data[m1]) {
data[m2], data[m1] = data[m1], data[m2]
// data[m0] <= data[m2] && data[m1] < data[m2]
if less(data[m1], data[m0]) {
data[m1], data[m0] = data[m0], data[m1]
}
}
// now data[m0] <= data[m1] <= data[m2]
}
func swapRangeLessFunc[Elem any](data []Elem, a, b, n int, less func(a, b Elem) bool) {
for i := 0; i < n; i++ {
data[a+i], data[b+i] = data[b+i], data[a+i]
}
}
func doPivotLessFunc[Elem any](data []Elem, lo, hi int, less func(a, b Elem) bool) (midlo, midhi int) {
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
if hi-lo > 40 {
// Tukey's "Ninther" median of three medians of three.
s := (hi - lo) / 8
medianOfThreeLessFunc(data, lo, lo+s, lo+2*s, less)
medianOfThreeLessFunc(data, m, m-s, m+s, less)
medianOfThreeLessFunc(data, hi-1, hi-1-s, hi-1-2*s, less)
}
medianOfThreeLessFunc(data, lo, m, hi-1, less)
// Invariants are:
// data[lo] = pivot (set up by ChoosePivot)
// data[lo < i < a] < pivot
// data[a <= i < b] <= pivot
// data[b <= i < c] unexamined
// data[c <= i < hi-1] > pivot
// data[hi-1] >= pivot
pivot := lo
a, c := lo+1, hi-1
for ; a < c && less(data[a], data[pivot]); a++ {
}
b := a
for {
for ; b < c && !less(data[pivot], data[b]); b++ { // data[b] <= pivot
}
for ; b < c && less(data[pivot], data[c-1]); c-- { // data[c-1] > pivot
}
if b >= c {
break
}
// data[b] > pivot; data[c-1] <= pivot
data[b], data[c-1] = data[c-1], data[b]
b++
c--
}
// If hi-c<3 then there are duplicates (by property of median of nine).
// Let's be a bit more conservative, and set border to 5.
protect := hi-c < 5
if !protect && hi-c < (hi-lo)/4 {
// Lets test some points for equality to pivot
dups := 0
if !less(data[pivot], data[hi-1]) { // data[hi-1] = pivot
data[c], data[hi-1] = data[hi-1], data[c]
c++
dups++
}
if !less(data[b-1], data[pivot]) { // data[b-1] = pivot
b--
dups++
}
// m-lo = (hi-lo)/2 > 6
// b-lo > (hi-lo)*3/4-1 > 8
// ==> m < b ==> data[m] <= pivot
if !less(data[m], data[pivot]) { // data[m] = pivot
data[m], data[b-1] = data[b-1], data[m]
b--
dups++
}
// if at least 2 points are equal to pivot, assume skewed distribution
protect = dups > 1
}
if protect {
// Protect against a lot of duplicates
// Add invariant:
// data[a <= i < b] unexamined
// data[b <= i < c] = pivot
for {
for ; a < b && !less(data[b-1], data[pivot]); b-- { // data[b] == pivot
}
for ; a < b && less(data[a], data[pivot]); a++ { // data[a] < pivot
}
if a >= b {
break
}
// data[a] == pivot; data[b-1] < pivot
data[a], data[b-1] = data[b-1], data[a]
a++
b--
}
}
// Swap pivot into middle
data[pivot], data[b-1] = data[b-1], data[pivot]
return b - 1, c
}
func quickSortLessFunc[Elem any](data []Elem, a, b, maxDepth int, less func(a, b Elem) bool) {
for b-a > 12 { // Use ShellSort for slices <= 12 elements
if maxDepth == 0 {
heapSortLessFunc(data, a, b, less)
return
}
maxDepth--
mlo, mhi := doPivotLessFunc(data, a, b, less)
// Avoiding recursion on the larger subproblem guarantees
// a stack depth of at most lg(b-a).
if mlo-a < b-mhi {
quickSortLessFunc(data, a, mlo, maxDepth, less)
a = mhi // i.e., quickSortLessFunc(data, mhi, b)
} else {
quickSortLessFunc(data, mhi, b, maxDepth, less)
b = mlo // i.e., quickSortLessFunc(data, a, mlo)
}
}
if b-a > 1 {
// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
if less(data[i], data[i-6]) {
data[i], data[i-6] = data[i-6], data[i]
}
}
insertionSortLessFunc(data, a, b, less)
}
}
func stableLessFunc[Elem any](data []Elem, n int, less func(a, b Elem) bool) {
blockSize := 20 // must be > 0
a, b := 0, blockSize
for b <= n {
insertionSortLessFunc(data, a, b, less)
a = b
b += blockSize
}
insertionSortLessFunc(data, a, n, less)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
symMergeLessFunc(data, a, a+blockSize, b, less)
a = b
b += 2 * blockSize
}
if m := a + blockSize; m < n {
symMergeLessFunc(data, a, m, n, less)
}
blockSize *= 2
}
}
// symMergeLessFunc merges the two sorted subsequences data[a:m] and data[m:b] using
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
// Computer Science, pages 714-723. Springer, 2004.
//
// Let M = m-a and N = b-n. Wolog M < N.
// The recursion depth is bound by ceil(log(N+M)).
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
//
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
// in the paper carries through for Swap operations, especially as the block
// swapping rotate uses only O(M+N) Swaps.
//
// symMerge assumes non-degenerate arguments: a < m && m < b.
// Having the caller check this condition eliminates many leaf recursion calls,
// which improves performance.
func symMergeLessFunc[Elem any](data []Elem, a, m, b int, less func(a, b Elem) bool) {
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[a] into data[m:b]
// if data[a:m] only contains one element.
if m-a == 1 {
// Use binary search to find the lowest index i
// such that data[i] >= data[a] for m <= i < b.
// Exit the search loop with i == b in case no such index exists.
i := m
j := b
for i < j {
h := int(uint(i+j) >> 1)
if less(data[h], data[a]) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[a] reaches the position before i.
for k := a; k < i-1; k++ {
data[k], data[k+1] = data[k+1], data[k]
}
return
}
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[m] into data[a:m]
// if data[m:b] only contains one element.
if b-m == 1 {
// Use binary search to find the lowest index i
// such that data[i] > data[m] for a <= i < m.
// Exit the search loop with i == m in case no such index exists.
i := a
j := m
for i < j {
h := int(uint(i+j) >> 1)
if !less(data[m], data[h]) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[m] reaches the position i.
for k := m; k > i; k-- {
data[k], data[k-1] = data[k-1], data[k]
}
return
}
mid := int(uint(a+b) >> 1)
n := mid + m
var start, r int
if m > mid {
start = n - b
r = mid
} else {
start = a
r = m
}
p := n - 1
for start < r {
c := int(uint(start+r) >> 1)
if !less(data[p-c], data[c]) {
start = c + 1
} else {
r = c
}
}
end := n - start
if start < m && m < end {
rotateLessFunc(data, start, m, end, less)
}
if a < start && start < mid {
symMergeLessFunc(data, a, start, mid, less)
}
if mid < end && end < b {
symMergeLessFunc(data, mid, end, b, less)
}
}
// rotateLessFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
// Data of the form 'x u v y' is changed to 'x v u y'.
// rotate performs at most b-a many calls to data.Swap,
// and it assumes non-degenerate arguments: a < m && m < b.
func rotateLessFunc[Elem any](data []Elem, a, m, b int, less func(a, b Elem) bool) {
i := m - a
j := b - m
for i != j {
if i > j {
swapRangeLessFunc(data, m-i, m, j, less)
i -= j
} else {
swapRangeLessFunc(data, m-i, m+j-i, i, less)
j -= i
}
}
// i == j
swapRangeLessFunc(data, m-i, m, i, less)
}

344
vendor/golang.org/x/exp/slices/zsortordered.go generated vendored Normal file
View file

@ -0,0 +1,344 @@
// Code generated by gen_sort_variants.go; DO NOT EDIT.
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package slices
import "golang.org/x/exp/constraints"
// insertionSortOrdered sorts data[a:b] using insertion sort.
func insertionSortOrdered[Elem constraints.Ordered](data []Elem, a, b int) {
for i := a + 1; i < b; i++ {
for j := i; j > a && (data[j] < data[j-1]); j-- {
data[j], data[j-1] = data[j-1], data[j]
}
}
}
// siftDownOrdered implements the heap property on data[lo:hi].
// first is an offset into the array where the root of the heap lies.
func siftDownOrdered[Elem constraints.Ordered](data []Elem, lo, hi, first int) {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && (data[first+child] < data[first+child+1]) {
child++
}
if !(data[first+root] < data[first+child]) {
return
}
data[first+root], data[first+child] = data[first+child], data[first+root]
root = child
}
}
func heapSortOrdered[Elem constraints.Ordered](data []Elem, a, b int) {
first := a
lo := 0
hi := b - a
// Build heap with greatest element at top.
for i := (hi - 1) / 2; i >= 0; i-- {
siftDownOrdered(data, i, hi, first)
}
// Pop elements, largest first, into end of data.
for i := hi - 1; i >= 0; i-- {
data[first], data[first+i] = data[first+i], data[first]
siftDownOrdered(data, lo, i, first)
}
}
// Quicksort, loosely following Bentley and McIlroy,
// "Engineering a Sort Function" SP&E November 1993.
// medianOfThreeOrdered moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
func medianOfThreeOrdered[Elem constraints.Ordered](data []Elem, m1, m0, m2 int) {
// sort 3 elements
if data[m1] < data[m0] {
data[m1], data[m0] = data[m0], data[m1]
}
// data[m0] <= data[m1]
if data[m2] < data[m1] {
data[m2], data[m1] = data[m1], data[m2]
// data[m0] <= data[m2] && data[m1] < data[m2]
if data[m1] < data[m0] {
data[m1], data[m0] = data[m0], data[m1]
}
}
// now data[m0] <= data[m1] <= data[m2]
}
func swapRangeOrdered[Elem constraints.Ordered](data []Elem, a, b, n int) {
for i := 0; i < n; i++ {
data[a+i], data[b+i] = data[b+i], data[a+i]
}
}
func doPivotOrdered[Elem constraints.Ordered](data []Elem, lo, hi int) (midlo, midhi int) {
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
if hi-lo > 40 {
// Tukey's "Ninther" median of three medians of three.
s := (hi - lo) / 8
medianOfThreeOrdered(data, lo, lo+s, lo+2*s)
medianOfThreeOrdered(data, m, m-s, m+s)
medianOfThreeOrdered(data, hi-1, hi-1-s, hi-1-2*s)
}
medianOfThreeOrdered(data, lo, m, hi-1)
// Invariants are:
// data[lo] = pivot (set up by ChoosePivot)
// data[lo < i < a] < pivot
// data[a <= i < b] <= pivot
// data[b <= i < c] unexamined
// data[c <= i < hi-1] > pivot
// data[hi-1] >= pivot
pivot := lo
a, c := lo+1, hi-1
for ; a < c && (data[a] < data[pivot]); a++ {
}
b := a
for {
for ; b < c && !(data[pivot] < data[b]); b++ { // data[b] <= pivot
}
for ; b < c && (data[pivot] < data[c-1]); c-- { // data[c-1] > pivot
}
if b >= c {
break
}
// data[b] > pivot; data[c-1] <= pivot
data[b], data[c-1] = data[c-1], data[b]
b++
c--
}
// If hi-c<3 then there are duplicates (by property of median of nine).
// Let's be a bit more conservative, and set border to 5.
protect := hi-c < 5
if !protect && hi-c < (hi-lo)/4 {
// Lets test some points for equality to pivot
dups := 0
if !(data[pivot] < data[hi-1]) { // data[hi-1] = pivot
data[c], data[hi-1] = data[hi-1], data[c]
c++
dups++
}
if !(data[b-1] < data[pivot]) { // data[b-1] = pivot
b--
dups++
}
// m-lo = (hi-lo)/2 > 6
// b-lo > (hi-lo)*3/4-1 > 8
// ==> m < b ==> data[m] <= pivot
if !(data[m] < data[pivot]) { // data[m] = pivot
data[m], data[b-1] = data[b-1], data[m]
b--
dups++
}
// if at least 2 points are equal to pivot, assume skewed distribution
protect = dups > 1
}
if protect {
// Protect against a lot of duplicates
// Add invariant:
// data[a <= i < b] unexamined
// data[b <= i < c] = pivot
for {
for ; a < b && !(data[b-1] < data[pivot]); b-- { // data[b] == pivot
}
for ; a < b && (data[a] < data[pivot]); a++ { // data[a] < pivot
}
if a >= b {
break
}
// data[a] == pivot; data[b-1] < pivot
data[a], data[b-1] = data[b-1], data[a]
a++
b--
}
}
// Swap pivot into middle
data[pivot], data[b-1] = data[b-1], data[pivot]
return b - 1, c
}
func quickSortOrdered[Elem constraints.Ordered](data []Elem, a, b, maxDepth int) {
for b-a > 12 { // Use ShellSort for slices <= 12 elements
if maxDepth == 0 {
heapSortOrdered(data, a, b)
return
}
maxDepth--
mlo, mhi := doPivotOrdered(data, a, b)
// Avoiding recursion on the larger subproblem guarantees
// a stack depth of at most lg(b-a).
if mlo-a < b-mhi {
quickSortOrdered(data, a, mlo, maxDepth)
a = mhi // i.e., quickSortOrdered(data, mhi, b)
} else {
quickSortOrdered(data, mhi, b, maxDepth)
b = mlo // i.e., quickSortOrdered(data, a, mlo)
}
}
if b-a > 1 {
// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
if data[i] < data[i-6] {
data[i], data[i-6] = data[i-6], data[i]
}
}
insertionSortOrdered(data, a, b)
}
}
func stableOrdered[Elem constraints.Ordered](data []Elem, n int) {
blockSize := 20 // must be > 0
a, b := 0, blockSize
for b <= n {
insertionSortOrdered(data, a, b)
a = b
b += blockSize
}
insertionSortOrdered(data, a, n)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
symMergeOrdered(data, a, a+blockSize, b)
a = b
b += 2 * blockSize
}
if m := a + blockSize; m < n {
symMergeOrdered(data, a, m, n)
}
blockSize *= 2
}
}
// symMergeOrdered merges the two sorted subsequences data[a:m] and data[m:b] using
// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
// Computer Science, pages 714-723. Springer, 2004.
//
// Let M = m-a and N = b-n. Wolog M < N.
// The recursion depth is bound by ceil(log(N+M)).
// The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
// The algorithm needs O((M+N)*log(M)) calls to data.Swap.
//
// The paper gives O((M+N)*log(M)) as the number of assignments assuming a
// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
// in the paper carries through for Swap operations, especially as the block
// swapping rotate uses only O(M+N) Swaps.
//
// symMerge assumes non-degenerate arguments: a < m && m < b.
// Having the caller check this condition eliminates many leaf recursion calls,
// which improves performance.
func symMergeOrdered[Elem constraints.Ordered](data []Elem, a, m, b int) {
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[a] into data[m:b]
// if data[a:m] only contains one element.
if m-a == 1 {
// Use binary search to find the lowest index i
// such that data[i] >= data[a] for m <= i < b.
// Exit the search loop with i == b in case no such index exists.
i := m
j := b
for i < j {
h := int(uint(i+j) >> 1)
if data[h] < data[a] {
i = h + 1
} else {
j = h
}
}
// Swap values until data[a] reaches the position before i.
for k := a; k < i-1; k++ {
data[k], data[k+1] = data[k+1], data[k]
}
return
}
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[m] into data[a:m]
// if data[m:b] only contains one element.
if b-m == 1 {
// Use binary search to find the lowest index i
// such that data[i] > data[m] for a <= i < m.
// Exit the search loop with i == m in case no such index exists.
i := a
j := m
for i < j {
h := int(uint(i+j) >> 1)
if !(data[m] < data[h]) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[m] reaches the position i.
for k := m; k > i; k-- {
data[k], data[k-1] = data[k-1], data[k]
}
return
}
mid := int(uint(a+b) >> 1)
n := mid + m
var start, r int
if m > mid {
start = n - b
r = mid
} else {
start = a
r = m
}
p := n - 1
for start < r {
c := int(uint(start+r) >> 1)
if !(data[p-c] < data[c]) {
start = c + 1
} else {
r = c
}
}
end := n - start
if start < m && m < end {
rotateOrdered(data, start, m, end)
}
if a < start && start < mid {
symMergeOrdered(data, a, start, mid)
}
if mid < end && end < b {
symMergeOrdered(data, mid, end, b)
}
}
// rotateOrdered rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
// Data of the form 'x u v y' is changed to 'x v u y'.
// rotate performs at most b-a many calls to data.Swap,
// and it assumes non-degenerate arguments: a < m && m < b.
func rotateOrdered[Elem constraints.Ordered](data []Elem, a, m, b int) {
i := m - a
j := b - m
for i != j {
if i > j {
swapRangeOrdered(data, m-i, m, j)
i -= j
} else {
swapRangeOrdered(data, m-i, m+j-i, i)
j -= i
}
}
// i == j
swapRangeOrdered(data, m-i, m, i)
}

5
vendor/modules.txt vendored
View file

@ -1,6 +1,11 @@
# github.com/go-logr/logr v1.2.2 # github.com/go-logr/logr v1.2.2
## explicit; go 1.16 ## explicit; go 1.16
github.com/go-logr/logr github.com/go-logr/logr
# golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7
## explicit; go 1.18
golang.org/x/exp/constraints
golang.org/x/exp/maps
golang.org/x/exp/slices
# golang.org/x/net v0.0.0-20220225172249-27dd8689420f # golang.org/x/net v0.0.0-20220225172249-27dd8689420f
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/net/webdav golang.org/x/net/webdav