mirror of
https://github.com/containers/skopeo.git
synced 2025-06-28 07:37:41 +00:00
commit
7afbf30963
@ -74,7 +74,7 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
|||||||
rawManifest []byte
|
rawManifest []byte
|
||||||
src types.ImageSource
|
src types.ImageSource
|
||||||
imgInspect *types.ImageInspectInfo
|
imgInspect *types.ImageInspectInfo
|
||||||
data []interface{}
|
data []any
|
||||||
)
|
)
|
||||||
ctx, cancel := opts.global.commandTimeoutContext()
|
ctx, cancel := opts.global.commandTimeoutContext()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -240,7 +240,7 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
|||||||
return printTmpl(stdout, row, data)
|
return printTmpl(stdout, row, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printTmpl(stdout io.Writer, row string, data []interface{}) error {
|
func printTmpl(stdout io.Writer, row string, data []any) error {
|
||||||
t, err := template.New("skopeo inspect").Parse(row)
|
t, err := template.New("skopeo inspect").Parse(row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/containers/image/v5/transports/alltransports"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
// tagListOutput is the output format of (skopeo list-tags), primarily so that we can format it with a simple json.MarshalIndent.
|
// tagListOutput is the output format of (skopeo list-tags), primarily so that we can format it with a simple json.MarshalIndent.
|
||||||
@ -37,10 +38,7 @@ var transportHandlers = map[string]func(ctx context.Context, sys *types.SystemCo
|
|||||||
|
|
||||||
// supportedTransports returns all the supported transports
|
// supportedTransports returns all the supported transports
|
||||||
func supportedTransports(joinStr string) string {
|
func supportedTransports(joinStr string) string {
|
||||||
res := make([]string, 0, len(transportHandlers))
|
res := maps.Keys(transportHandlers)
|
||||||
for handlerName := range transportHandlers {
|
|
||||||
res = append(res, handlerName)
|
|
||||||
}
|
|
||||||
sort.Strings(res)
|
sort.Strings(res)
|
||||||
return strings.Join(res, joinStr)
|
return strings.Join(res, joinStr)
|
||||||
}
|
}
|
||||||
@ -84,12 +82,12 @@ func parseDockerRepositoryReference(refString string) (types.ImageReference, err
|
|||||||
return nil, fmt.Errorf("docker: image reference %s does not start with %s://", refString, docker.Transport.Name())
|
return nil, fmt.Errorf("docker: image reference %s does not start with %s://", refString, docker.Transport.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.SplitN(refString, ":", 2)
|
_, dockerImageName, hasColon := strings.Cut(refString, ":")
|
||||||
if len(parts) != 2 {
|
if !hasColon {
|
||||||
return nil, fmt.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, refString)
|
return nil, fmt.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, refString)
|
||||||
}
|
}
|
||||||
|
|
||||||
ref, err := reference.ParseNormalizedNamed(strings.TrimPrefix(parts[1], "//"))
|
ref, err := reference.ParseNormalizedNamed(strings.TrimPrefix(dockerImageName, "//"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ type request struct {
|
|||||||
// Method is the name of the function
|
// Method is the name of the function
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
// Args is the arguments (parsed inside the function)
|
// Args is the arguments (parsed inside the function)
|
||||||
Args []interface{} `json:"args"`
|
Args []any `json:"args"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply is serialized to JSON as the return value from a function call.
|
// reply is serialized to JSON as the return value from a function call.
|
||||||
@ -123,7 +123,7 @@ type reply struct {
|
|||||||
// Success is true if and only if the call succeeded.
|
// Success is true if and only if the call succeeded.
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
// Value is an arbitrary value (or values, as array/map) returned from the call.
|
// Value is an arbitrary value (or values, as array/map) returned from the call.
|
||||||
Value interface{} `json:"value"`
|
Value any `json:"value"`
|
||||||
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
||||||
PipeID uint32 `json:"pipeid"`
|
PipeID uint32 `json:"pipeid"`
|
||||||
// Error should be non-empty if Success == false
|
// Error should be non-empty if Success == false
|
||||||
@ -133,7 +133,7 @@ type reply struct {
|
|||||||
// replyBuf is our internal deserialization of reply plus optional fd
|
// replyBuf is our internal deserialization of reply plus optional fd
|
||||||
type replyBuf struct {
|
type replyBuf struct {
|
||||||
// value will be converted to a reply Value
|
// value will be converted to a reply Value
|
||||||
value interface{}
|
value any
|
||||||
// fd is the read half of a pipe, passed back to the client
|
// fd is the read half of a pipe, passed back to the client
|
||||||
fd *os.File
|
fd *os.File
|
||||||
// pipeid will be provided to the client as PipeID, an index into our open pipes
|
// pipeid will be provided to the client as PipeID, an index into our open pipes
|
||||||
@ -185,7 +185,7 @@ type convertedLayerInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize performs one-time initialization, and returns the protocol version
|
// Initialize performs one-time initialization, and returns the protocol version
|
||||||
func (h *proxyHandler) Initialize(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) Initialize(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ func (h *proxyHandler) Initialize(args []interface{}) (replyBuf, error) {
|
|||||||
|
|
||||||
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
||||||
// The return value is an opaque integer handle.
|
// The return value is an opaque integer handle.
|
||||||
func (h *proxyHandler) OpenImage(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) OpenImage(args []any) (replyBuf, error) {
|
||||||
return h.openImageImpl(args, false)
|
return h.openImageImpl(args, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ func isNotFoundImageError(err error) bool {
|
|||||||
errors.Is(err, ocilayout.ImageNotFoundError{})
|
errors.Is(err, ocilayout.ImageNotFoundError{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (replyBuf, error) {
|
func (h *proxyHandler) openImageImpl(args []any, allowNotFound bool) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
var ret replyBuf
|
var ret replyBuf
|
||||||
@ -282,11 +282,11 @@ func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (re
|
|||||||
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
||||||
// The return value is an opaque integer handle. If the image does not exist, zero
|
// The return value is an opaque integer handle. If the image does not exist, zero
|
||||||
// is returned.
|
// is returned.
|
||||||
func (h *proxyHandler) OpenImageOptional(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) OpenImageOptional(args []any) (replyBuf, error) {
|
||||||
return h.openImageImpl(args, true)
|
return h.openImageImpl(args, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *proxyHandler) CloseImage(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) CloseImage(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
var ret replyBuf
|
var ret replyBuf
|
||||||
@ -307,7 +307,7 @@ func (h *proxyHandler) CloseImage(args []interface{}) (replyBuf, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseImageID(v interface{}) (uint32, error) {
|
func parseImageID(v any) (uint32, error) {
|
||||||
imgidf, ok := v.(float64)
|
imgidf, ok := v.(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("expecting integer imageid, not %T", v)
|
return 0, fmt.Errorf("expecting integer imageid, not %T", v)
|
||||||
@ -316,7 +316,7 @@ func parseImageID(v interface{}) (uint32, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseUint64 validates that a number fits inside a JavaScript safe integer
|
// parseUint64 validates that a number fits inside a JavaScript safe integer
|
||||||
func parseUint64(v interface{}) (uint64, error) {
|
func parseUint64(v any) (uint64, error) {
|
||||||
f, ok := v.(float64)
|
f, ok := v.(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("expecting numeric, not %T", v)
|
return 0, fmt.Errorf("expecting numeric, not %T", v)
|
||||||
@ -327,7 +327,7 @@ func parseUint64(v interface{}) (uint64, error) {
|
|||||||
return uint64(f), nil
|
return uint64(f), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *proxyHandler) parseImageFromID(v interface{}) (*openImage, error) {
|
func (h *proxyHandler) parseImageFromID(v any) (*openImage, error) {
|
||||||
imgid, err := parseImageID(v)
|
imgid, err := parseImageID(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -357,7 +357,7 @@ func (h *proxyHandler) allocPipe() (*os.File, *activePipe, error) {
|
|||||||
|
|
||||||
// returnBytes generates a return pipe() from a byte array
|
// returnBytes generates a return pipe() from a byte array
|
||||||
// In the future it might be nicer to return this via memfd_create()
|
// In the future it might be nicer to return this via memfd_create()
|
||||||
func (h *proxyHandler) returnBytes(retval interface{}, buf []byte) (replyBuf, error) {
|
func (h *proxyHandler) returnBytes(retval any, buf []byte) (replyBuf, error) {
|
||||||
var ret replyBuf
|
var ret replyBuf
|
||||||
piper, f, err := h.allocPipe()
|
piper, f, err := h.allocPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -419,7 +419,7 @@ func (h *proxyHandler) cacheTargetManifest(img *openImage) error {
|
|||||||
|
|
||||||
// GetManifest returns a copy of the manifest, converted to OCI format, along with the original digest.
|
// GetManifest returns a copy of the manifest, converted to OCI format, along with the original digest.
|
||||||
// Manifest lists are resolved to the current operating system and architecture.
|
// Manifest lists are resolved to the current operating system and architecture.
|
||||||
func (h *proxyHandler) GetManifest(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) GetManifest(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
@ -490,7 +490,7 @@ func (h *proxyHandler) GetManifest(args []interface{}) (replyBuf, error) {
|
|||||||
|
|
||||||
// GetFullConfig returns a copy of the image configuration, converted to OCI format.
|
// GetFullConfig returns a copy of the image configuration, converted to OCI format.
|
||||||
// https://github.com/opencontainers/image-spec/blob/main/config.md
|
// https://github.com/opencontainers/image-spec/blob/main/config.md
|
||||||
func (h *proxyHandler) GetFullConfig(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) GetFullConfig(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
@ -527,7 +527,7 @@ func (h *proxyHandler) GetFullConfig(args []interface{}) (replyBuf, error) {
|
|||||||
// GetConfig returns a copy of the container runtime configuration, converted to OCI format.
|
// GetConfig returns a copy of the container runtime configuration, converted to OCI format.
|
||||||
// Note that due to a historical mistake, this returns not the full image configuration,
|
// Note that due to a historical mistake, this returns not the full image configuration,
|
||||||
// but just the container runtime configuration. You should use GetFullConfig instead.
|
// but just the container runtime configuration. You should use GetFullConfig instead.
|
||||||
func (h *proxyHandler) GetConfig(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) GetConfig(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
@ -562,7 +562,7 @@ func (h *proxyHandler) GetConfig(args []interface{}) (replyBuf, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetBlob fetches a blob, performing digest verification.
|
// GetBlob fetches a blob, performing digest verification.
|
||||||
func (h *proxyHandler) GetBlob(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) GetBlob(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
@ -632,7 +632,7 @@ func (h *proxyHandler) GetBlob(args []interface{}) (replyBuf, error) {
|
|||||||
// This needs to be called since the data returned by GetManifest() does not allow to correctly
|
// This needs to be called since the data returned by GetManifest() does not allow to correctly
|
||||||
// calling GetBlob() for the containers-storage: transport (which doesn’t store the original compressed
|
// calling GetBlob() for the containers-storage: transport (which doesn’t store the original compressed
|
||||||
// representations referenced in the manifest).
|
// representations referenced in the manifest).
|
||||||
func (h *proxyHandler) GetLayerInfo(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
@ -678,7 +678,7 @@ func (h *proxyHandler) GetLayerInfo(args []interface{}) (replyBuf, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FinishPipe waits for the worker goroutine to finish, and closes the write side of the pipe.
|
// FinishPipe waits for the worker goroutine to finish, and closes the write side of the pipe.
|
||||||
func (h *proxyHandler) FinishPipe(args []interface{}) (replyBuf, error) {
|
func (h *proxyHandler) FinishPipe(args []any) (replyBuf, error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -523,26 +524,17 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) (retErr error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// validate source and destination options
|
// validate source and destination options
|
||||||
contains := func(val string, list []string) (_ bool) {
|
|
||||||
for _, l := range list {
|
|
||||||
if l == val {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.source) == 0 {
|
if len(opts.source) == 0 {
|
||||||
return errors.New("A source transport must be specified")
|
return errors.New("A source transport must be specified")
|
||||||
}
|
}
|
||||||
if !contains(opts.source, []string{docker.Transport.Name(), directory.Transport.Name(), "yaml"}) {
|
if !slices.Contains([]string{docker.Transport.Name(), directory.Transport.Name(), "yaml"}, opts.source) {
|
||||||
return fmt.Errorf("%q is not a valid source transport", opts.source)
|
return fmt.Errorf("%q is not a valid source transport", opts.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.destination) == 0 {
|
if len(opts.destination) == 0 {
|
||||||
return errors.New("A destination transport must be specified")
|
return errors.New("A destination transport must be specified")
|
||||||
}
|
}
|
||||||
if !contains(opts.destination, []string{docker.Transport.Name(), directory.Transport.Name()}) {
|
if !slices.Contains([]string{docker.Transport.Name(), directory.Transport.Name()}, opts.destination) {
|
||||||
return fmt.Errorf("%q is not a valid destination transport", opts.destination)
|
return fmt.Errorf("%q is not a valid destination transport", opts.destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/containers/image/v5/transports/alltransports"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/storage/pkg/unshare"
|
"github.com/containers/storage/pkg/unshare"
|
||||||
"github.com/syndtr/gocapability/capability"
|
"github.com/syndtr/gocapability/capability"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
var neededCapabilities = []capability.Cap{
|
var neededCapabilities = []capability.Cap{
|
||||||
@ -25,25 +26,25 @@ func maybeReexec() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading the current capabilities sets: %w", err)
|
return fmt.Errorf("error reading the current capabilities sets: %w", err)
|
||||||
}
|
}
|
||||||
for _, cap := range neededCapabilities {
|
if slices.ContainsFunc(neededCapabilities, func(cap capability.Cap) bool {
|
||||||
if !capabilities.Get(capability.EFFECTIVE, cap) {
|
return !capabilities.Get(capability.EFFECTIVE, cap)
|
||||||
// We miss a capability we need, create a user namespaces
|
}) {
|
||||||
unshare.MaybeReexecUsingUserNamespace(true)
|
// We miss a capability we need, create a user namespaces
|
||||||
return nil
|
unshare.MaybeReexecUsingUserNamespace(true)
|
||||||
}
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func reexecIfNecessaryForImages(imageNames ...string) error {
|
func reexecIfNecessaryForImages(imageNames ...string) error {
|
||||||
// Check if container-storage is used before doing unshare
|
// Check if container-storage is used before doing unshare
|
||||||
for _, imageName := range imageNames {
|
if slices.ContainsFunc(imageNames, func(imageName string) bool {
|
||||||
transport := alltransports.TransportFromImageName(imageName)
|
transport := alltransports.TransportFromImageName(imageName)
|
||||||
// Hard-code the storage name to avoid a reference on c/image/storage.
|
// Hard-code the storage name to avoid a reference on c/image/storage.
|
||||||
// See https://github.com/containers/skopeo/issues/771#issuecomment-563125006.
|
// See https://github.com/containers/skopeo/issues/771#issuecomment-563125006.
|
||||||
if transport != nil && transport.Name() == "containers-storage" {
|
return transport != nil && transport.Name() == "containers-storage"
|
||||||
return maybeReexec()
|
}) {
|
||||||
}
|
return maybeReexec()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -315,14 +315,11 @@ func parseCreds(creds string) (string, string, error) {
|
|||||||
if creds == "" {
|
if creds == "" {
|
||||||
return "", "", errors.New("credentials can't be empty")
|
return "", "", errors.New("credentials can't be empty")
|
||||||
}
|
}
|
||||||
up := strings.SplitN(creds, ":", 2)
|
username, password, _ := strings.Cut(creds, ":") // Sets password to "" if there is no ":"
|
||||||
if len(up) == 1 {
|
if username == "" {
|
||||||
return up[0], "", nil
|
|
||||||
}
|
|
||||||
if up[0] == "" {
|
|
||||||
return "", "", errors.New("username can't be empty")
|
return "", "", errors.New("username can't be empty")
|
||||||
}
|
}
|
||||||
return up[0], up[1], nil
|
return username, password, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDockerAuth(creds string) (*types.DockerAuthConfig, error) {
|
func getDockerAuth(creds string) (*types.DockerAuthConfig, error) {
|
||||||
|
3
go.mod
3
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/containers/skopeo
|
module github.com/containers/skopeo
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/containers/common v0.51.0
|
github.com/containers/common v0.51.0
|
||||||
@ -16,6 +16,7 @@ require (
|
|||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||||
|
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
|
||||||
golang.org/x/term v0.4.0
|
golang.org/x/term v0.4.0
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
@ -607,12 +607,12 @@ func assertDirImagesAreEqual(c *check.C, dir1, dir2 string) {
|
|||||||
// Check whether schema1 dir: images in dir1 and dir2 are equal, ignoring schema1 signatures and the embedded path/tag values, which should have the expected values.
|
// Check whether schema1 dir: images in dir1 and dir2 are equal, ignoring schema1 signatures and the embedded path/tag values, which should have the expected values.
|
||||||
func assertSchema1DirImagesAreEqualExceptNames(c *check.C, dir1, ref1, dir2, ref2 string) {
|
func assertSchema1DirImagesAreEqualExceptNames(c *check.C, dir1, ref1, dir2, ref2 string) {
|
||||||
// The manifests may have different JWS signatures and names; so, unmarshal and delete these elements.
|
// The manifests may have different JWS signatures and names; so, unmarshal and delete these elements.
|
||||||
manifests := []map[string]interface{}{}
|
manifests := []map[string]any{}
|
||||||
for dir, ref := range map[string]string{dir1: ref1, dir2: ref2} {
|
for dir, ref := range map[string]string{dir1: ref1, dir2: ref2} {
|
||||||
manifestPath := filepath.Join(dir, "manifest.json")
|
manifestPath := filepath.Join(dir, "manifest.json")
|
||||||
m, err := os.ReadFile(manifestPath)
|
m, err := os.ReadFile(manifestPath)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
data := map[string]interface{}{}
|
data := map[string]any{}
|
||||||
err = json.Unmarshal(m, &data)
|
err = json.Unmarshal(m, &data)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(data["schemaVersion"], check.Equals, float64(1))
|
c.Assert(data["schemaVersion"], check.Equals, float64(1))
|
||||||
|
@ -154,7 +154,7 @@ func (cluster *openshiftCluster) prepareRegistryConfig(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startRegistry starts the OpenShift registry with configPart on port, waits for it to be ready, and returns the process object, or terminates on failure.
|
// startRegistry starts the OpenShift registry with configPart on port, waits for it to be ready, and returns the process object, or terminates on failure.
|
||||||
func (cluster *openshiftCluster) startRegistryProcess(c *check.C, port int, configPath string) *exec.Cmd {
|
func (cluster *openshiftCluster) startRegistryProcess(c *check.C, port uint16, configPath string) *exec.Cmd {
|
||||||
cmd := cluster.clusterCmd(map[string]string{
|
cmd := cluster.clusterCmd(map[string]string{
|
||||||
"KUBECONFIG": "openshift.local.registry/openshift-registry.kubeconfig",
|
"KUBECONFIG": "openshift.local.registry/openshift-registry.kubeconfig",
|
||||||
"DOCKER_REGISTRY_URL": fmt.Sprintf("127.0.0.1:%d", port),
|
"DOCKER_REGISTRY_URL": fmt.Sprintf("127.0.0.1:%d", port),
|
||||||
|
@ -32,7 +32,7 @@ type request struct {
|
|||||||
// Method is the name of the function
|
// Method is the name of the function
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
// Args is the arguments (parsed inside the function)
|
// Args is the arguments (parsed inside the function)
|
||||||
Args []interface{} `json:"args"`
|
Args []any `json:"args"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply is copied from proxy.go
|
// reply is copied from proxy.go
|
||||||
@ -40,7 +40,7 @@ type reply struct {
|
|||||||
// Success is true if and only if the call succeeded.
|
// Success is true if and only if the call succeeded.
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
// Value is an arbitrary value (or values, as array/map) returned from the call.
|
// Value is an arbitrary value (or values, as array/map) returned from the call.
|
||||||
Value interface{} `json:"value"`
|
Value any `json:"value"`
|
||||||
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
||||||
PipeID uint32 `json:"pipeid"`
|
PipeID uint32 `json:"pipeid"`
|
||||||
// Error should be non-empty if Success == false
|
// Error should be non-empty if Success == false
|
||||||
@ -60,7 +60,7 @@ type pipefd struct {
|
|||||||
fd *os.File
|
fd *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proxy) call(method string, args []interface{}) (rval interface{}, fd *pipefd, err error) {
|
func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error) {
|
||||||
req := request{
|
req := request{
|
||||||
Method: method,
|
Method: method,
|
||||||
Args: args,
|
Args: args,
|
||||||
@ -122,7 +122,7 @@ func (p *proxy) call(method string, args []interface{}) (rval interface{}, fd *p
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proxy) callNoFd(method string, args []interface{}) (rval interface{}, err error) {
|
func (p *proxy) callNoFd(method string, args []any) (rval any, err error) {
|
||||||
var fd *pipefd
|
var fd *pipefd
|
||||||
rval, fd, err = p.call(method, args)
|
rval, fd, err = p.call(method, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -135,7 +135,7 @@ func (p *proxy) callNoFd(method string, args []interface{}) (rval interface{}, e
|
|||||||
return rval, nil
|
return rval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proxy) callReadAllBytes(method string, args []interface{}) (rval interface{}, buf []byte, err error) {
|
func (p *proxy) callReadAllBytes(method string, args []any) (rval any, buf []byte, err error) {
|
||||||
var fd *pipefd
|
var fd *pipefd
|
||||||
rval, fd, err = p.call(method, args)
|
rval, fd, err = p.call(method, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -153,7 +153,7 @@ func (p *proxy) callReadAllBytes(method string, args []interface{}) (rval interf
|
|||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
_, err = p.callNoFd("FinishPipe", []interface{}{fd.id})
|
_, err = p.callNoFd("FinishPipe", []any{fd.id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ type byteFetch struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runTestGetManifestAndConfig(p *proxy, img string) error {
|
func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||||
v, err := p.callNoFd("OpenImage", []interface{}{knownNotManifestListedImage_x8664})
|
v, err := p.callNoFd("OpenImage", []any{knownNotManifestListedImage_x8664})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -248,7 +248,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Also verify the optional path
|
// Also verify the optional path
|
||||||
v, err = p.callNoFd("OpenImageOptional", []interface{}{knownNotManifestListedImage_x8664})
|
v, err = p.callNoFd("OpenImageOptional", []any{knownNotManifestListedImage_x8664})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -262,12 +262,12 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
|||||||
return fmt.Errorf("got zero from expected image")
|
return fmt.Errorf("got zero from expected image")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = p.callNoFd("CloseImage", []interface{}{imgid2})
|
_, err = p.callNoFd("CloseImage", []any{imgid2})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, manifestBytes, err := p.callReadAllBytes("GetManifest", []interface{}{imgid})
|
_, manifestBytes, err := p.callReadAllBytes("GetManifest", []any{imgid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -276,7 +276,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, configBytes, err := p.callReadAllBytes("GetFullConfig", []interface{}{imgid})
|
_, configBytes, err := p.callReadAllBytes("GetFullConfig", []any{imgid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -295,7 +295,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Also test this legacy interface
|
// Also test this legacy interface
|
||||||
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []interface{}{imgid})
|
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []any{imgid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -310,7 +310,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
|||||||
return fmt.Errorf("No CMD or ENTRYPOINT set")
|
return fmt.Errorf("No CMD or ENTRYPOINT set")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = p.callNoFd("CloseImage", []interface{}{imgid})
|
_, err = p.callNoFd("CloseImage", []any{imgid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -319,7 +319,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runTestOpenImageOptionalNotFound(p *proxy, img string) error {
|
func runTestOpenImageOptionalNotFound(p *proxy, img string) error {
|
||||||
v, err := p.callNoFd("OpenImageOptional", []interface{}{img})
|
v, err := p.callNoFd("OpenImageOptional", []any{img})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -105,8 +106,9 @@ func runExecCmdWithInput(c *check.C, cmd *exec.Cmd, input string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isPortOpen returns true iff the specified port on localhost is open.
|
// isPortOpen returns true iff the specified port on localhost is open.
|
||||||
func isPortOpen(port int) bool {
|
func isPortOpen(port uint16) bool {
|
||||||
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port})
|
ap := netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), port)
|
||||||
|
conn, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(ap))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -118,7 +120,7 @@ func isPortOpen(port int) bool {
|
|||||||
// The checking can be aborted by sending a value to the terminate channel, which the caller should
|
// The checking can be aborted by sending a value to the terminate channel, which the caller should
|
||||||
// always do using
|
// always do using
|
||||||
// defer func() {terminate <- true}()
|
// defer func() {terminate <- true}()
|
||||||
func newPortChecker(c *check.C, port int) (portOpen <-chan bool, terminate chan<- bool) {
|
func newPortChecker(c *check.C, port uint16) (portOpen <-chan bool, terminate chan<- bool) {
|
||||||
portOpenBidi := make(chan bool)
|
portOpenBidi := make(chan bool)
|
||||||
// Buffered, so that sending a terminate request after the goroutine has exited does not block.
|
// Buffered, so that sending a terminate request after the goroutine has exited does not block.
|
||||||
terminateBidi := make(chan bool, 1)
|
terminateBidi := make(chan bool, 1)
|
||||||
|
27
vendor/golang.org/x/exp/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/exp/LICENSE
generated
vendored
Normal 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
22
vendor/golang.org/x/exp/PATENTS
generated
vendored
Normal 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
50
vendor/golang.org/x/exp/constraints/constraints.go
generated
vendored
Normal 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
|
||||||
|
}
|
94
vendor/golang.org/x/exp/maps/maps.go
generated
vendored
Normal file
94
vendor/golang.org/x/exp/maps/maps.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// 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 {
|
||||||
|
// Preserve nil in case it matters.
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
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[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
258
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
Normal file
258
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
// 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).
|
||||||
|
//
|
||||||
|
// Note that the less function in IsSortedFunc, SortFunc, SortStableFunc requires a
|
||||||
|
// strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings),
|
||||||
|
// or the sorting may fail to sort correctly. A common case is when sorting slices of
|
||||||
|
// floating-point numbers containing NaN values.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsFunc reports whether at least one
|
||||||
|
// element e of s satisfies f(e).
|
||||||
|
func ContainsFunc[E any](s []E, f func(E) bool) bool {
|
||||||
|
return IndexFunc(s, f) >= 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), 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.
|
||||||
|
// Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those
|
||||||
|
// elements contain pointers you might consider zeroing those elements so that
|
||||||
|
// objects they reference can be garbage collected.
|
||||||
|
func Delete[S ~[]E, E any](s S, i, j int) S {
|
||||||
|
_ = s[i:j] // bounds check
|
||||||
|
|
||||||
|
return append(s[:i], s[j:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace replaces the elements s[i:j] by the given v, and returns the
|
||||||
|
// modified slice. Replace panics if s[i:j] is not a valid slice of s.
|
||||||
|
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
||||||
|
_ = s[i:j] // verify that i:j is a valid subslice
|
||||||
|
tot := len(s[:i]) + len(v) + len(s[j:])
|
||||||
|
if tot <= cap(s) {
|
||||||
|
s2 := s[:tot]
|
||||||
|
copy(s2[i+len(v):], s[j:])
|
||||||
|
copy(s2[i:], v)
|
||||||
|
return s2
|
||||||
|
}
|
||||||
|
s2 := make(S, tot)
|
||||||
|
copy(s2, s[:i])
|
||||||
|
copy(s2[i:], v)
|
||||||
|
copy(s2[i+len(v):], s[j:])
|
||||||
|
return s2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// When Compact discards m elements in total, it might not modify the elements
|
||||||
|
// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
|
||||||
|
// zeroing those elements so that objects they reference can be garbage collected.
|
||||||
|
func Compact[S ~[]E, E comparable](s S) S {
|
||||||
|
if len(s) < 2 {
|
||||||
|
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) < 2 {
|
||||||
|
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. If n is negative or too large to
|
||||||
|
// allocate the memory, Grow panics.
|
||||||
|
func Grow[S ~[]E, E any](s S, n int) S {
|
||||||
|
if n < 0 {
|
||||||
|
panic("cannot be negative")
|
||||||
|
}
|
||||||
|
if n -= cap(s) - len(s); n > 0 {
|
||||||
|
// TODO(https://go.dev/issue/53888): Make using []E instead of S
|
||||||
|
// to workaround a compiler bug where the runtime.growslice optimization
|
||||||
|
// does not take effect. Revert when the compiler is fixed.
|
||||||
|
s = append([]E(s)[:cap(s)], make([]E, n)...)[:len(s)]
|
||||||
|
}
|
||||||
|
return 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)]
|
||||||
|
}
|
126
vendor/golang.org/x/exp/slices/sort.go
generated
vendored
Normal file
126
vendor/golang.org/x/exp/slices/sort.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// 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 (
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort sorts a slice of any ordered type in ascending order.
|
||||||
|
// Sort may fail to sort correctly when sorting slices of floating-point
|
||||||
|
// numbers containing Not-a-number (NaN) values.
|
||||||
|
// Use slices.SortFunc(x, func(a, b float64) bool {return a < b || (math.IsNaN(a) && !math.IsNaN(b))})
|
||||||
|
// instead if the input may contain NaNs.
|
||||||
|
func Sort[E constraints.Ordered](x []E) {
|
||||||
|
n := len(x)
|
||||||
|
pdqsortOrdered(x, 0, n, bits.Len(uint(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortFunc sorts the slice x in ascending order as determined by the less function.
|
||||||
|
// This sort is not guaranteed to be stable.
|
||||||
|
//
|
||||||
|
// SortFunc requires that less is a strict weak ordering.
|
||||||
|
// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
|
||||||
|
func SortFunc[E any](x []E, less func(a, b E) bool) {
|
||||||
|
n := len(x)
|
||||||
|
pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortStableFunc sorts the slice x while keeping the original order of equal
|
||||||
|
// elements, using less to compare elements.
|
||||||
|
func SortStableFunc[E any](x []E, less func(a, b E) bool) {
|
||||||
|
stableLessFunc(x, len(x), less)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSorted reports whether x is sorted in ascending order.
|
||||||
|
func IsSorted[E constraints.Ordered](x []E) 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[E any](x []E, less func(a, b E) 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 position
|
||||||
|
// where target is found, or the position where target would appear in the
|
||||||
|
// sort order; it also returns a bool saying whether the target is really found
|
||||||
|
// in the slice. The slice must be sorted in increasing order.
|
||||||
|
func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) {
|
||||||
|
// Inlining is faster than calling BinarySearchFunc with a lambda.
|
||||||
|
n := len(x)
|
||||||
|
// Define x[-1] < target and x[n] >= target.
|
||||||
|
// Invariant: x[i-1] < target, x[j] >= target.
|
||||||
|
i, j := 0, n
|
||||||
|
for i < j {
|
||||||
|
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||||
|
// i ≤ h < j
|
||||||
|
if x[h] < target {
|
||||||
|
i = h + 1 // preserves x[i-1] < target
|
||||||
|
} else {
|
||||||
|
j = h // preserves x[j] >= target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i.
|
||||||
|
return i, i < n && x[i] == target
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinarySearchFunc works like BinarySearch, but uses a custom comparison
|
||||||
|
// function. The slice must be sorted in increasing order, where "increasing" is
|
||||||
|
// defined by cmp. cmp(a, b) is expected to return an integer comparing the two
|
||||||
|
// parameters: 0 if a == b, a negative number if a < b and a positive number if
|
||||||
|
// a > b.
|
||||||
|
func BinarySearchFunc[E any](x []E, target E, cmp func(E, E) int) (int, bool) {
|
||||||
|
n := len(x)
|
||||||
|
// Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
|
||||||
|
// Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
|
||||||
|
i, j := 0, n
|
||||||
|
for i < j {
|
||||||
|
h := int(uint(i+j) >> 1) // avoid overflow when computing h
|
||||||
|
// i ≤ h < j
|
||||||
|
if cmp(x[h], target) < 0 {
|
||||||
|
i = h + 1 // preserves cmp(x[i - 1], target) < 0
|
||||||
|
} else {
|
||||||
|
j = h // preserves cmp(x[j], target) >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i.
|
||||||
|
return i, i < n && cmp(x[i], target) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortedHint int // hint for pdqsort when choosing the pivot
|
||||||
|
|
||||||
|
const (
|
||||||
|
unknownHint sortedHint = iota
|
||||||
|
increasingHint
|
||||||
|
decreasingHint
|
||||||
|
)
|
||||||
|
|
||||||
|
// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
|
||||||
|
type xorshift uint64
|
||||||
|
|
||||||
|
func (r *xorshift) Next() uint64 {
|
||||||
|
*r ^= *r << 13
|
||||||
|
*r ^= *r >> 17
|
||||||
|
*r ^= *r << 5
|
||||||
|
return uint64(*r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextPowerOfTwo(length int) uint {
|
||||||
|
return 1 << bits.Len(uint(length))
|
||||||
|
}
|
479
vendor/golang.org/x/exp/slices/zsortfunc.go
generated
vendored
Normal file
479
vendor/golang.org/x/exp/slices/zsortfunc.go
generated
vendored
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
// 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[E any](data []E, a, b int, less func(a, b E) 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[E any](data []E, lo, hi, first int, less func(a, b E) 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[E any](data []E, a, b int, less func(a, b E) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pdqsortLessFunc sorts data[a:b].
|
||||||
|
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
||||||
|
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
||||||
|
// C++ implementation: https://github.com/orlp/pdqsort
|
||||||
|
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
||||||
|
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
||||||
|
func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) {
|
||||||
|
const maxInsertion = 12
|
||||||
|
|
||||||
|
var (
|
||||||
|
wasBalanced = true // whether the last partitioning was reasonably balanced
|
||||||
|
wasPartitioned = true // whether the slice was already partitioned
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
length := b - a
|
||||||
|
|
||||||
|
if length <= maxInsertion {
|
||||||
|
insertionSortLessFunc(data, a, b, less)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to heapsort if too many bad choices were made.
|
||||||
|
if limit == 0 {
|
||||||
|
heapSortLessFunc(data, a, b, less)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last partitioning was imbalanced, we need to breaking patterns.
|
||||||
|
if !wasBalanced {
|
||||||
|
breakPatternsLessFunc(data, a, b, less)
|
||||||
|
limit--
|
||||||
|
}
|
||||||
|
|
||||||
|
pivot, hint := choosePivotLessFunc(data, a, b, less)
|
||||||
|
if hint == decreasingHint {
|
||||||
|
reverseRangeLessFunc(data, a, b, less)
|
||||||
|
// The chosen pivot was pivot-a elements after the start of the array.
|
||||||
|
// After reversing it is pivot-a elements before the end of the array.
|
||||||
|
// The idea came from Rust's implementation.
|
||||||
|
pivot = (b - 1) - (pivot - a)
|
||||||
|
hint = increasingHint
|
||||||
|
}
|
||||||
|
|
||||||
|
// The slice is likely already sorted.
|
||||||
|
if wasBalanced && wasPartitioned && hint == increasingHint {
|
||||||
|
if partialInsertionSortLessFunc(data, a, b, less) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probably the slice contains many duplicate elements, partition the slice into
|
||||||
|
// elements equal to and elements greater than the pivot.
|
||||||
|
if a > 0 && !less(data[a-1], data[pivot]) {
|
||||||
|
mid := partitionEqualLessFunc(data, a, b, pivot, less)
|
||||||
|
a = mid
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mid, alreadyPartitioned := partitionLessFunc(data, a, b, pivot, less)
|
||||||
|
wasPartitioned = alreadyPartitioned
|
||||||
|
|
||||||
|
leftLen, rightLen := mid-a, b-mid
|
||||||
|
balanceThreshold := length / 8
|
||||||
|
if leftLen < rightLen {
|
||||||
|
wasBalanced = leftLen >= balanceThreshold
|
||||||
|
pdqsortLessFunc(data, a, mid, limit, less)
|
||||||
|
a = mid + 1
|
||||||
|
} else {
|
||||||
|
wasBalanced = rightLen >= balanceThreshold
|
||||||
|
pdqsortLessFunc(data, mid+1, b, limit, less)
|
||||||
|
b = mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// partitionLessFunc does one quicksort partition.
|
||||||
|
// Let p = data[pivot]
|
||||||
|
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
||||||
|
// On return, data[newpivot] = p
|
||||||
|
func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int, alreadyPartitioned bool) {
|
||||||
|
data[a], data[pivot] = data[pivot], data[a]
|
||||||
|
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||||
|
|
||||||
|
for i <= j && less(data[i], data[a]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i <= j && !less(data[j], data[a]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
if i > j {
|
||||||
|
data[j], data[a] = data[a], data[j]
|
||||||
|
return j, true
|
||||||
|
}
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
|
||||||
|
for {
|
||||||
|
for i <= j && less(data[i], data[a]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i <= j && !less(data[j], data[a]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
if i > j {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
data[j], data[a] = data[a], data[j]
|
||||||
|
return j, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// partitionEqualLessFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
||||||
|
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
||||||
|
func partitionEqualLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int) {
|
||||||
|
data[a], data[pivot] = data[pivot], data[a]
|
||||||
|
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||||
|
|
||||||
|
for {
|
||||||
|
for i <= j && !less(data[a], data[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i <= j && less(data[a], data[j]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
if i > j {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// partialInsertionSortLessFunc partially sorts a slice, returns true if the slice is sorted at the end.
|
||||||
|
func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) bool {
|
||||||
|
const (
|
||||||
|
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
||||||
|
shortestShifting = 50 // don't shift any elements on short arrays
|
||||||
|
)
|
||||||
|
i := a + 1
|
||||||
|
for j := 0; j < maxSteps; j++ {
|
||||||
|
for i < b && !less(data[i], data[i-1]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if b-a < shortestShifting {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
data[i], data[i-1] = data[i-1], data[i]
|
||||||
|
|
||||||
|
// Shift the smaller one to the left.
|
||||||
|
if i-a >= 2 {
|
||||||
|
for j := i - 1; j >= 1; j-- {
|
||||||
|
if !less(data[j], data[j-1]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Shift the greater one to the right.
|
||||||
|
if b-i >= 2 {
|
||||||
|
for j := i + 1; j < b; j++ {
|
||||||
|
if !less(data[j], data[j-1]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// breakPatternsLessFunc scatters some elements around in an attempt to break some patterns
|
||||||
|
// that might cause imbalanced partitions in quicksort.
|
||||||
|
func breakPatternsLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
|
||||||
|
length := b - a
|
||||||
|
if length >= 8 {
|
||||||
|
random := xorshift(length)
|
||||||
|
modulus := nextPowerOfTwo(length)
|
||||||
|
|
||||||
|
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
||||||
|
other := int(uint(random.Next()) & (modulus - 1))
|
||||||
|
if other >= length {
|
||||||
|
other -= length
|
||||||
|
}
|
||||||
|
data[idx], data[a+other] = data[a+other], data[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// choosePivotLessFunc chooses a pivot in data[a:b].
|
||||||
|
//
|
||||||
|
// [0,8): chooses a static pivot.
|
||||||
|
// [8,shortestNinther): uses the simple median-of-three method.
|
||||||
|
// [shortestNinther,∞): uses the Tukey ninther method.
|
||||||
|
func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (pivot int, hint sortedHint) {
|
||||||
|
const (
|
||||||
|
shortestNinther = 50
|
||||||
|
maxSwaps = 4 * 3
|
||||||
|
)
|
||||||
|
|
||||||
|
l := b - a
|
||||||
|
|
||||||
|
var (
|
||||||
|
swaps int
|
||||||
|
i = a + l/4*1
|
||||||
|
j = a + l/4*2
|
||||||
|
k = a + l/4*3
|
||||||
|
)
|
||||||
|
|
||||||
|
if l >= 8 {
|
||||||
|
if l >= shortestNinther {
|
||||||
|
// Tukey ninther method, the idea came from Rust's implementation.
|
||||||
|
i = medianAdjacentLessFunc(data, i, &swaps, less)
|
||||||
|
j = medianAdjacentLessFunc(data, j, &swaps, less)
|
||||||
|
k = medianAdjacentLessFunc(data, k, &swaps, less)
|
||||||
|
}
|
||||||
|
// Find the median among i, j, k and stores it into j.
|
||||||
|
j = medianLessFunc(data, i, j, k, &swaps, less)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch swaps {
|
||||||
|
case 0:
|
||||||
|
return j, increasingHint
|
||||||
|
case maxSwaps:
|
||||||
|
return j, decreasingHint
|
||||||
|
default:
|
||||||
|
return j, unknownHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// order2LessFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
||||||
|
func order2LessFunc[E any](data []E, a, b int, swaps *int, less func(a, b E) bool) (int, int) {
|
||||||
|
if less(data[b], data[a]) {
|
||||||
|
*swaps++
|
||||||
|
return b, a
|
||||||
|
}
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
|
||||||
|
// medianLessFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
||||||
|
func medianLessFunc[E any](data []E, a, b, c int, swaps *int, less func(a, b E) bool) int {
|
||||||
|
a, b = order2LessFunc(data, a, b, swaps, less)
|
||||||
|
b, c = order2LessFunc(data, b, c, swaps, less)
|
||||||
|
a, b = order2LessFunc(data, a, b, swaps, less)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// medianAdjacentLessFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
||||||
|
func medianAdjacentLessFunc[E any](data []E, a int, swaps *int, less func(a, b E) bool) int {
|
||||||
|
return medianLessFunc(data, a-1, a, a+1, swaps, less)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverseRangeLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
|
||||||
|
i := a
|
||||||
|
j := b - 1
|
||||||
|
for i < j {
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func swapRangeLessFunc[E any](data []E, a, b, n int, less func(a, b E) bool) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
data[a+i], data[b+i] = data[b+i], data[a+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stableLessFunc[E any](data []E, n int, less func(a, b E) 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[E any](data []E, a, m, b int, less func(a, b E) 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[E any](data []E, a, m, b int, less func(a, b E) 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)
|
||||||
|
}
|
481
vendor/golang.org/x/exp/slices/zsortordered.go
generated
vendored
Normal file
481
vendor/golang.org/x/exp/slices/zsortordered.go
generated
vendored
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
// 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[E constraints.Ordered](data []E, 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[E constraints.Ordered](data []E, 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[E constraints.Ordered](data []E, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pdqsortOrdered sorts data[a:b].
|
||||||
|
// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
|
||||||
|
// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
|
||||||
|
// C++ implementation: https://github.com/orlp/pdqsort
|
||||||
|
// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
|
||||||
|
// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
|
||||||
|
func pdqsortOrdered[E constraints.Ordered](data []E, a, b, limit int) {
|
||||||
|
const maxInsertion = 12
|
||||||
|
|
||||||
|
var (
|
||||||
|
wasBalanced = true // whether the last partitioning was reasonably balanced
|
||||||
|
wasPartitioned = true // whether the slice was already partitioned
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
length := b - a
|
||||||
|
|
||||||
|
if length <= maxInsertion {
|
||||||
|
insertionSortOrdered(data, a, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to heapsort if too many bad choices were made.
|
||||||
|
if limit == 0 {
|
||||||
|
heapSortOrdered(data, a, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last partitioning was imbalanced, we need to breaking patterns.
|
||||||
|
if !wasBalanced {
|
||||||
|
breakPatternsOrdered(data, a, b)
|
||||||
|
limit--
|
||||||
|
}
|
||||||
|
|
||||||
|
pivot, hint := choosePivotOrdered(data, a, b)
|
||||||
|
if hint == decreasingHint {
|
||||||
|
reverseRangeOrdered(data, a, b)
|
||||||
|
// The chosen pivot was pivot-a elements after the start of the array.
|
||||||
|
// After reversing it is pivot-a elements before the end of the array.
|
||||||
|
// The idea came from Rust's implementation.
|
||||||
|
pivot = (b - 1) - (pivot - a)
|
||||||
|
hint = increasingHint
|
||||||
|
}
|
||||||
|
|
||||||
|
// The slice is likely already sorted.
|
||||||
|
if wasBalanced && wasPartitioned && hint == increasingHint {
|
||||||
|
if partialInsertionSortOrdered(data, a, b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probably the slice contains many duplicate elements, partition the slice into
|
||||||
|
// elements equal to and elements greater than the pivot.
|
||||||
|
if a > 0 && !(data[a-1] < data[pivot]) {
|
||||||
|
mid := partitionEqualOrdered(data, a, b, pivot)
|
||||||
|
a = mid
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mid, alreadyPartitioned := partitionOrdered(data, a, b, pivot)
|
||||||
|
wasPartitioned = alreadyPartitioned
|
||||||
|
|
||||||
|
leftLen, rightLen := mid-a, b-mid
|
||||||
|
balanceThreshold := length / 8
|
||||||
|
if leftLen < rightLen {
|
||||||
|
wasBalanced = leftLen >= balanceThreshold
|
||||||
|
pdqsortOrdered(data, a, mid, limit)
|
||||||
|
a = mid + 1
|
||||||
|
} else {
|
||||||
|
wasBalanced = rightLen >= balanceThreshold
|
||||||
|
pdqsortOrdered(data, mid+1, b, limit)
|
||||||
|
b = mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// partitionOrdered does one quicksort partition.
|
||||||
|
// Let p = data[pivot]
|
||||||
|
// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
|
||||||
|
// On return, data[newpivot] = p
|
||||||
|
func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) {
|
||||||
|
data[a], data[pivot] = data[pivot], data[a]
|
||||||
|
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||||
|
|
||||||
|
for i <= j && (data[i] < data[a]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i <= j && !(data[j] < data[a]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
if i > j {
|
||||||
|
data[j], data[a] = data[a], data[j]
|
||||||
|
return j, true
|
||||||
|
}
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
|
||||||
|
for {
|
||||||
|
for i <= j && (data[i] < data[a]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i <= j && !(data[j] < data[a]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
if i > j {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
data[j], data[a] = data[a], data[j]
|
||||||
|
return j, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// partitionEqualOrdered partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
|
||||||
|
// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
|
||||||
|
func partitionEqualOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int) {
|
||||||
|
data[a], data[pivot] = data[pivot], data[a]
|
||||||
|
i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
|
||||||
|
|
||||||
|
for {
|
||||||
|
for i <= j && !(data[a] < data[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for i <= j && (data[a] < data[j]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
if i > j {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// partialInsertionSortOrdered partially sorts a slice, returns true if the slice is sorted at the end.
|
||||||
|
func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool {
|
||||||
|
const (
|
||||||
|
maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
|
||||||
|
shortestShifting = 50 // don't shift any elements on short arrays
|
||||||
|
)
|
||||||
|
i := a + 1
|
||||||
|
for j := 0; j < maxSteps; j++ {
|
||||||
|
for i < b && !(data[i] < data[i-1]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if b-a < shortestShifting {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
data[i], data[i-1] = data[i-1], data[i]
|
||||||
|
|
||||||
|
// Shift the smaller one to the left.
|
||||||
|
if i-a >= 2 {
|
||||||
|
for j := i - 1; j >= 1; j-- {
|
||||||
|
if !(data[j] < data[j-1]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Shift the greater one to the right.
|
||||||
|
if b-i >= 2 {
|
||||||
|
for j := i + 1; j < b; j++ {
|
||||||
|
if !(data[j] < data[j-1]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// breakPatternsOrdered scatters some elements around in an attempt to break some patterns
|
||||||
|
// that might cause imbalanced partitions in quicksort.
|
||||||
|
func breakPatternsOrdered[E constraints.Ordered](data []E, a, b int) {
|
||||||
|
length := b - a
|
||||||
|
if length >= 8 {
|
||||||
|
random := xorshift(length)
|
||||||
|
modulus := nextPowerOfTwo(length)
|
||||||
|
|
||||||
|
for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
|
||||||
|
other := int(uint(random.Next()) & (modulus - 1))
|
||||||
|
if other >= length {
|
||||||
|
other -= length
|
||||||
|
}
|
||||||
|
data[idx], data[a+other] = data[a+other], data[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// choosePivotOrdered chooses a pivot in data[a:b].
|
||||||
|
//
|
||||||
|
// [0,8): chooses a static pivot.
|
||||||
|
// [8,shortestNinther): uses the simple median-of-three method.
|
||||||
|
// [shortestNinther,∞): uses the Tukey ninther method.
|
||||||
|
func choosePivotOrdered[E constraints.Ordered](data []E, a, b int) (pivot int, hint sortedHint) {
|
||||||
|
const (
|
||||||
|
shortestNinther = 50
|
||||||
|
maxSwaps = 4 * 3
|
||||||
|
)
|
||||||
|
|
||||||
|
l := b - a
|
||||||
|
|
||||||
|
var (
|
||||||
|
swaps int
|
||||||
|
i = a + l/4*1
|
||||||
|
j = a + l/4*2
|
||||||
|
k = a + l/4*3
|
||||||
|
)
|
||||||
|
|
||||||
|
if l >= 8 {
|
||||||
|
if l >= shortestNinther {
|
||||||
|
// Tukey ninther method, the idea came from Rust's implementation.
|
||||||
|
i = medianAdjacentOrdered(data, i, &swaps)
|
||||||
|
j = medianAdjacentOrdered(data, j, &swaps)
|
||||||
|
k = medianAdjacentOrdered(data, k, &swaps)
|
||||||
|
}
|
||||||
|
// Find the median among i, j, k and stores it into j.
|
||||||
|
j = medianOrdered(data, i, j, k, &swaps)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch swaps {
|
||||||
|
case 0:
|
||||||
|
return j, increasingHint
|
||||||
|
case maxSwaps:
|
||||||
|
return j, decreasingHint
|
||||||
|
default:
|
||||||
|
return j, unknownHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
|
||||||
|
func order2Ordered[E constraints.Ordered](data []E, a, b int, swaps *int) (int, int) {
|
||||||
|
if data[b] < data[a] {
|
||||||
|
*swaps++
|
||||||
|
return b, a
|
||||||
|
}
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
|
||||||
|
// medianOrdered returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
|
||||||
|
func medianOrdered[E constraints.Ordered](data []E, a, b, c int, swaps *int) int {
|
||||||
|
a, b = order2Ordered(data, a, b, swaps)
|
||||||
|
b, c = order2Ordered(data, b, c, swaps)
|
||||||
|
a, b = order2Ordered(data, a, b, swaps)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// medianAdjacentOrdered finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
|
||||||
|
func medianAdjacentOrdered[E constraints.Ordered](data []E, a int, swaps *int) int {
|
||||||
|
return medianOrdered(data, a-1, a, a+1, swaps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverseRangeOrdered[E constraints.Ordered](data []E, a, b int) {
|
||||||
|
i := a
|
||||||
|
j := b - 1
|
||||||
|
for i < j {
|
||||||
|
data[i], data[j] = data[j], data[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func swapRangeOrdered[E constraints.Ordered](data []E, a, b, n int) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
data[a+i], data[b+i] = data[b+i], data[a+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stableOrdered[E constraints.Ordered](data []E, 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[E constraints.Ordered](data []E, 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[E constraints.Ordered](data []E, 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
5
vendor/modules.txt
vendored
@ -610,6 +610,11 @@ golang.org/x/crypto/pbkdf2
|
|||||||
golang.org/x/crypto/salsa20/salsa
|
golang.org/x/crypto/salsa20/salsa
|
||||||
golang.org/x/crypto/scrypt
|
golang.org/x/crypto/scrypt
|
||||||
golang.org/x/crypto/sha3
|
golang.org/x/crypto/sha3
|
||||||
|
# golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
|
||||||
|
## explicit; go 1.18
|
||||||
|
golang.org/x/exp/constraints
|
||||||
|
golang.org/x/exp/maps
|
||||||
|
golang.org/x/exp/slices
|
||||||
# golang.org/x/mod v0.7.0
|
# golang.org/x/mod v0.7.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
|
Loading…
Reference in New Issue
Block a user