distro/rhel: add payload repos to os package set
Before instantiating the manifest, any repositories that contain a package set key contained in `PayloadPackageSets()` should be added to the os package set in order to correctly generate the `org.osbuild.rpm` stage for the os pipeline. Otherwise options like GPG keys are not set correctly. Fixes #3326
This commit is contained in:
parent
f731ab53d0
commit
03973acc1f
25 changed files with 2081 additions and 194 deletions
5
go.mod
5
go.mod
|
|
@ -44,6 +44,7 @@ require (
|
|||
github.com/stretchr/testify v1.8.1
|
||||
github.com/ubccr/kerby v0.0.0-20170626144437-201a958fc453
|
||||
github.com/vmware/govmomi v0.29.0
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
golang.org/x/sys v0.3.0
|
||||
|
|
@ -149,12 +150,12 @@ require (
|
|||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.2.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/time v0.2.0 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
|
|
|
|||
10
go.sum
10
go.sum
|
|
@ -1464,6 +1464,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
|
@ -1491,8 +1493,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -1876,8 +1878,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
|||
|
|
@ -157,8 +157,16 @@ func TestImageType_PackageSetsChains(t *testing.T) {
|
|||
// results.
|
||||
func TestImageTypePipelineNames(t *testing.T) {
|
||||
// types for parsing the opaque manifest with just the fields we care about
|
||||
type rpmStageOptions struct {
|
||||
GPGKeys []string `json:"gpgkeys"`
|
||||
}
|
||||
type stage struct {
|
||||
Type string `json:"type"`
|
||||
Options rpmStageOptions `json:"options"`
|
||||
}
|
||||
type pipeline struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name"`
|
||||
Stages []stage `json:"stages"`
|
||||
}
|
||||
type manifest struct {
|
||||
Pipelines []pipeline `json:"pipelines"`
|
||||
|
|
@ -187,7 +195,17 @@ func TestImageTypePipelineNames(t *testing.T) {
|
|||
Customizations: customizations,
|
||||
}
|
||||
options := distro.ImageOptions{}
|
||||
repos := make([]rpmmd.RepoConfig, 0)
|
||||
// this repo's gpg keys should get included in the os
|
||||
// pipeline's rpm stage
|
||||
repos := []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "payload",
|
||||
BaseURL: "http://payload.example.com",
|
||||
PackageSets: imageType.PayloadPackageSets(),
|
||||
GPGKeys: []string{"payload-gpg-key"},
|
||||
CheckGPG: true,
|
||||
},
|
||||
}
|
||||
containers := make([]container.Spec, 0)
|
||||
seed := int64(0)
|
||||
|
||||
|
|
@ -224,6 +242,18 @@ func TestImageTypePipelineNames(t *testing.T) {
|
|||
// manifest pipeline names should be identical to the ones
|
||||
// defined in the image type and in the same order
|
||||
require.Equal(allPipelines[idx], pm.Pipelines[idx].Name)
|
||||
|
||||
if pm.Pipelines[idx].Name == "os" {
|
||||
rpmStagePresent := false
|
||||
for _, s := range pm.Pipelines[idx].Stages {
|
||||
if s.Type == "org.osbuild.rpm" {
|
||||
rpmStagePresent = true
|
||||
require.Equal(repos[0].GPGKeys, s.Options.GPGKeys)
|
||||
}
|
||||
}
|
||||
// make sure the gpg keys check was reached
|
||||
require.True(rpmStagePresent)
|
||||
}
|
||||
}
|
||||
|
||||
// The last pipeline should match the export pipeline.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
|
|
@ -736,7 +737,25 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations,
|
|||
}
|
||||
bp.Customizations = customizations
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, repos, nil, containers, seed)
|
||||
// the os pipeline filters repos based on the `osPkgsKey` package set, merge the repos which
|
||||
// contain a payload package set into the `osPkgsKey`, so those repos are included when
|
||||
// building the rpm stage in the os pipeline
|
||||
// TODO: roll this into workloads
|
||||
mergedRepos := make([]rpmmd.RepoConfig, 0, len(repos))
|
||||
for _, repo := range repos {
|
||||
for _, pkgsKey := range t.PayloadPackageSets() {
|
||||
// If the repo already contains the osPkgsKey, skip
|
||||
if slices.Contains(repo.PackageSets, osPkgsKey) {
|
||||
break
|
||||
}
|
||||
if slices.Contains(repo.PackageSets, pkgsKey) {
|
||||
repo.PackageSets = append(repo.PackageSets, osPkgsKey)
|
||||
}
|
||||
}
|
||||
mergedRepos = append(mergedRepos, repo)
|
||||
}
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, mergedRepos, nil, containers, seed)
|
||||
if err != nil {
|
||||
return distro.Manifest{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
|
|
@ -21,7 +24,6 @@ import (
|
|||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -391,7 +393,25 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations,
|
|||
}
|
||||
bp.Customizations = customizations
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, repos, nil, containers, seed)
|
||||
// the os pipeline filters repos based on the `osPkgsKey` package set, merge the repos which
|
||||
// contain a payload package set into the `osPkgsKey`, so those repos are included when
|
||||
// building the rpm stage in the os pipeline
|
||||
// TODO: roll this into workloads
|
||||
mergedRepos := make([]rpmmd.RepoConfig, 0, len(repos))
|
||||
for _, repo := range repos {
|
||||
for _, pkgsKey := range t.PayloadPackageSets() {
|
||||
// If the repo already contains the osPkgsKey, skip
|
||||
if slices.Contains(repo.PackageSets, osPkgsKey) {
|
||||
break
|
||||
}
|
||||
if slices.Contains(repo.PackageSets, pkgsKey) {
|
||||
repo.PackageSets = append(repo.PackageSets, osPkgsKey)
|
||||
}
|
||||
}
|
||||
mergedRepos = append(mergedRepos, repo)
|
||||
}
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, mergedRepos, nil, containers, seed)
|
||||
if err != nil {
|
||||
return distro.Manifest{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import (
|
|||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
|
|
@ -19,7 +22,6 @@ import (
|
|||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -238,7 +240,25 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations,
|
|||
}
|
||||
bp.Customizations = customizations
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, repos, nil, containers, seed)
|
||||
// the os pipeline filters repos based on the `osPkgsKey` package set, merge the repos which
|
||||
// contain a payload package set into the `osPkgsKey`, so those repos are included when
|
||||
// building the rpm stage in the os pipeline
|
||||
// TODO: roll this into workloads
|
||||
mergedRepos := make([]rpmmd.RepoConfig, 0, len(repos))
|
||||
for _, repo := range repos {
|
||||
for _, pkgsKey := range t.PayloadPackageSets() {
|
||||
// If the repo already contains the osPkgsKey, skip
|
||||
if slices.Contains(repo.PackageSets, osPkgsKey) {
|
||||
break
|
||||
}
|
||||
if slices.Contains(repo.PackageSets, pkgsKey) {
|
||||
repo.PackageSets = append(repo.PackageSets, osPkgsKey)
|
||||
}
|
||||
}
|
||||
mergedRepos = append(mergedRepos, repo)
|
||||
}
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, mergedRepos, nil, containers, seed)
|
||||
if err != nil {
|
||||
return distro.Manifest{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import (
|
|||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
|
|
@ -19,7 +22,6 @@ import (
|
|||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -238,7 +240,25 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations,
|
|||
}
|
||||
bp.Customizations = customizations
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, repos, nil, containers, seed)
|
||||
// the os pipeline filters repos based on the `osPkgsKey` package set, merge the repos which
|
||||
// contain a payload package set into the `osPkgsKey`, so those repos are included when
|
||||
// building the rpm stage in the os pipeline
|
||||
// TODO: roll this into workloads
|
||||
mergedRepos := make([]rpmmd.RepoConfig, 0, len(repos))
|
||||
for _, repo := range repos {
|
||||
for _, pkgsKey := range t.PayloadPackageSets() {
|
||||
// If the repo already contains the osPkgsKey, skip
|
||||
if slices.Contains(repo.PackageSets, osPkgsKey) {
|
||||
break
|
||||
}
|
||||
if slices.Contains(repo.PackageSets, pkgsKey) {
|
||||
repo.PackageSets = append(repo.PackageSets, osPkgsKey)
|
||||
}
|
||||
}
|
||||
mergedRepos = append(mergedRepos, repo)
|
||||
}
|
||||
|
||||
manifest, err := t.initializeManifest(bp, options, mergedRepos, nil, containers, seed)
|
||||
if err != nil {
|
||||
return distro.Manifest{}, err
|
||||
}
|
||||
|
|
|
|||
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
|
||||
}
|
||||
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, T any](x []E, target T, cmp func(E, T) 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)
|
||||
}
|
||||
4
vendor/golang.org/x/mod/module/module.go
generated
vendored
4
vendor/golang.org/x/mod/module/module.go
generated
vendored
|
|
@ -96,13 +96,13 @@ package module
|
|||
// Changes to the semantics in this file require approval from rsc.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
|
@ -258,7 +258,7 @@ func modPathOK(r rune) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// modPathOK reports whether r can appear in a package import path element.
|
||||
// importPathOK reports whether r can appear in a package import path element.
|
||||
//
|
||||
// Import paths are intermediate between module paths and file paths: we allow
|
||||
// disallow characters that would be confusing or ambiguous as arguments to
|
||||
|
|
|
|||
119
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_darwin.go
generated
vendored
Normal file
119
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_darwin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// 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.
|
||||
|
||||
//go:build darwin && cgo
|
||||
// +build darwin,cgo
|
||||
|
||||
package fastwalk
|
||||
|
||||
/*
|
||||
#include <dirent.h>
|
||||
|
||||
// fastwalk_readdir_r wraps readdir_r so that we don't have to pass a dirent**
|
||||
// result pointer which triggers CGO's "Go pointer to Go pointer" check unless
|
||||
// we allocat the result dirent* with malloc.
|
||||
//
|
||||
// fastwalk_readdir_r returns 0 on success, -1 upon reaching the end of the
|
||||
// directory, or a positive error number to indicate failure.
|
||||
static int fastwalk_readdir_r(DIR *fd, struct dirent *entry) {
|
||||
struct dirent *result;
|
||||
int ret = readdir_r(fd, entry, &result);
|
||||
if (ret == 0 && result == NULL) {
|
||||
ret = -1; // EOF
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
|
||||
fd, err := openDir(dirName)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "opendir", Path: dirName, Err: err}
|
||||
}
|
||||
defer C.closedir(fd)
|
||||
|
||||
skipFiles := false
|
||||
var dirent syscall.Dirent
|
||||
for {
|
||||
ret := int(C.fastwalk_readdir_r(fd, (*C.struct_dirent)(unsafe.Pointer(&dirent))))
|
||||
if ret != 0 {
|
||||
if ret == -1 {
|
||||
break // EOF
|
||||
}
|
||||
if ret == int(syscall.EINTR) {
|
||||
continue
|
||||
}
|
||||
return &os.PathError{Op: "readdir", Path: dirName, Err: syscall.Errno(ret)}
|
||||
}
|
||||
if dirent.Ino == 0 {
|
||||
continue
|
||||
}
|
||||
typ := dtToType(dirent.Type)
|
||||
if skipFiles && typ.IsRegular() {
|
||||
continue
|
||||
}
|
||||
name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
|
||||
name = name[:dirent.Namlen]
|
||||
for i, c := range name {
|
||||
if c == 0 {
|
||||
name = name[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check for useless names before allocating a string.
|
||||
if string(name) == "." || string(name) == ".." {
|
||||
continue
|
||||
}
|
||||
if err := fn(dirName, string(name), typ); err != nil {
|
||||
if err != ErrSkipFiles {
|
||||
return err
|
||||
}
|
||||
skipFiles = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dtToType(typ uint8) os.FileMode {
|
||||
switch typ {
|
||||
case syscall.DT_BLK:
|
||||
return os.ModeDevice
|
||||
case syscall.DT_CHR:
|
||||
return os.ModeDevice | os.ModeCharDevice
|
||||
case syscall.DT_DIR:
|
||||
return os.ModeDir
|
||||
case syscall.DT_FIFO:
|
||||
return os.ModeNamedPipe
|
||||
case syscall.DT_LNK:
|
||||
return os.ModeSymlink
|
||||
case syscall.DT_REG:
|
||||
return 0
|
||||
case syscall.DT_SOCK:
|
||||
return os.ModeSocket
|
||||
}
|
||||
return ^os.FileMode(0)
|
||||
}
|
||||
|
||||
// openDir wraps opendir(3) and handles any EINTR errors. The returned *DIR
|
||||
// needs to be closed with closedir(3).
|
||||
func openDir(path string) (*C.DIR, error) {
|
||||
name, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
fd, err := C.opendir((*C.char)(unsafe.Pointer(name)))
|
||||
if err != syscall.EINTR {
|
||||
return fd, err
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go
generated
vendored
6
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go
generated
vendored
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (linux || darwin) && !appengine
|
||||
// +build linux darwin
|
||||
//go:build (linux || (darwin && !cgo)) && !appengine
|
||||
// +build linux darwin,!cgo
|
||||
// +build !appengine
|
||||
|
||||
package fastwalk
|
||||
|
|
@ -11,5 +11,5 @@ package fastwalk
|
|||
import "syscall"
|
||||
|
||||
func direntInode(dirent *syscall.Dirent) uint64 {
|
||||
return uint64(dirent.Ino)
|
||||
return dirent.Ino
|
||||
}
|
||||
|
|
|
|||
4
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
generated
vendored
4
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_namlen_bsd.go
generated
vendored
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || freebsd || openbsd || netbsd
|
||||
// +build darwin freebsd openbsd netbsd
|
||||
//go:build (darwin && !cgo) || freebsd || openbsd || netbsd
|
||||
// +build darwin,!cgo freebsd openbsd netbsd
|
||||
|
||||
package fastwalk
|
||||
|
||||
|
|
|
|||
4
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go
generated
vendored
4
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go
generated
vendored
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (linux || darwin || freebsd || openbsd || netbsd) && !appengine
|
||||
// +build linux darwin freebsd openbsd netbsd
|
||||
//go:build (linux || freebsd || openbsd || netbsd || (darwin && !cgo)) && !appengine
|
||||
// +build linux freebsd openbsd netbsd darwin,!cgo
|
||||
// +build !appengine
|
||||
|
||||
package fastwalk
|
||||
|
|
|
|||
83
vendor/golang.org/x/tools/internal/gocommand/invoke.go
generated
vendored
83
vendor/golang.org/x/tools/internal/gocommand/invoke.go
generated
vendored
|
|
@ -10,8 +10,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -232,6 +234,12 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
|
|||
return runCmdContext(ctx, cmd)
|
||||
}
|
||||
|
||||
// DebugHangingGoCommands may be set by tests to enable additional
|
||||
// instrumentation (including panics) for debugging hanging Go commands.
|
||||
//
|
||||
// See golang/go#54461 for details.
|
||||
var DebugHangingGoCommands = false
|
||||
|
||||
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
|
||||
// before os.Kill.
|
||||
func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
||||
|
|
@ -243,11 +251,24 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
|||
resChan <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
// If we're interested in debugging hanging Go commands, stop waiting after a
|
||||
// minute and panic with interesting information.
|
||||
if DebugHangingGoCommands {
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-time.After(1 * time.Minute):
|
||||
HandleHangingGoCommand(cmd.Process)
|
||||
case <-ctx.Done():
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
// Cancelled. Interrupt and see if it ends voluntarily.
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
select {
|
||||
|
|
@ -255,11 +276,63 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
|||
return err
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
|
||||
// Didn't shut down in response to interrupt. Kill it hard.
|
||||
cmd.Process.Kill()
|
||||
// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT
|
||||
// on certain platforms, such as unix.
|
||||
if err := cmd.Process.Kill(); err != nil && DebugHangingGoCommands {
|
||||
// Don't panic here as this reliably fails on windows with EINVAL.
|
||||
log.Printf("error killing the Go command: %v", err)
|
||||
}
|
||||
|
||||
// See above: don't wait indefinitely if we're debugging hanging Go commands.
|
||||
if DebugHangingGoCommands {
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-time.After(10 * time.Second): // a shorter wait as resChan should return quickly following Kill
|
||||
HandleHangingGoCommand(cmd.Process)
|
||||
}
|
||||
}
|
||||
return <-resChan
|
||||
}
|
||||
|
||||
func HandleHangingGoCommand(proc *os.Process) {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "darwin", "freebsd", "netbsd":
|
||||
fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND
|
||||
|
||||
The gopls test runner has detected a hanging go command. In order to debug
|
||||
this, the output of ps and lsof/fstat is printed below.
|
||||
|
||||
See golang/go#54461 for more details.`)
|
||||
|
||||
fmt.Fprintln(os.Stderr, "\nps axo ppid,pid,command:")
|
||||
fmt.Fprintln(os.Stderr, "-------------------------")
|
||||
psCmd := exec.Command("ps", "axo", "ppid,pid,command")
|
||||
psCmd.Stdout = os.Stderr
|
||||
psCmd.Stderr = os.Stderr
|
||||
if err := psCmd.Run(); err != nil {
|
||||
panic(fmt.Sprintf("running ps: %v", err))
|
||||
}
|
||||
|
||||
listFiles := "lsof"
|
||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" {
|
||||
listFiles = "fstat"
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "\n"+listFiles+":")
|
||||
fmt.Fprintln(os.Stderr, "-----")
|
||||
listFilesCmd := exec.Command(listFiles)
|
||||
listFilesCmd.Stdout = os.Stderr
|
||||
listFilesCmd.Stderr = os.Stderr
|
||||
if err := listFilesCmd.Run(); err != nil {
|
||||
panic(fmt.Sprintf("running %s: %v", listFiles, err))
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid))
|
||||
}
|
||||
|
||||
func cmdDebugStr(cmd *exec.Cmd) string {
|
||||
env := make(map[string]string)
|
||||
for _, kv := range cmd.Env {
|
||||
|
|
|
|||
13
vendor/golang.org/x/tools/internal/gocommand/version.go
generated
vendored
13
vendor/golang.org/x/tools/internal/gocommand/version.go
generated
vendored
|
|
@ -10,8 +10,15 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// GoVersion checks the go version by running "go list" with modules off.
|
||||
// It returns the X in Go 1.X.
|
||||
// GoVersion reports the minor version number of the highest release
|
||||
// tag built into the go command on the PATH.
|
||||
//
|
||||
// Note that this may be higher than the version of the go tool used
|
||||
// to build this application, and thus the versions of the standard
|
||||
// go/{scanner,parser,ast,types} packages that are linked into it.
|
||||
// In that case, callers should either downgrade to the version of
|
||||
// go used to build the application, or report an error that the
|
||||
// application is too old to use the go command on the PATH.
|
||||
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
||||
inv.Verb = "list"
|
||||
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
|
||||
|
|
@ -38,7 +45,7 @@ func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
|||
if len(stdout) < 3 {
|
||||
return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
|
||||
}
|
||||
// Split up "[go1.1 go1.15]"
|
||||
// Split up "[go1.1 go1.15]" and return highest go1.X value.
|
||||
tags := strings.Fields(stdout[1 : len(stdout)-2])
|
||||
for i := len(tags) - 1; i >= 0; i-- {
|
||||
var version int
|
||||
|
|
|
|||
9
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
9
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
|
|
@ -807,6 +807,11 @@ type ProcessEnv struct {
|
|||
ModFlag string
|
||||
ModFile string
|
||||
|
||||
// SkipPathInScan returns true if the path should be skipped from scans of
|
||||
// the RootCurrentModule root type. The function argument is a clean,
|
||||
// absolute path.
|
||||
SkipPathInScan func(string) bool
|
||||
|
||||
// Env overrides the OS environment, and can be used to specify
|
||||
// GOPROXY, GO111MODULE, etc. PATH cannot be set here, because
|
||||
// exec.Command will not honor it.
|
||||
|
|
@ -1367,9 +1372,9 @@ func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error
|
|||
return err
|
||||
}
|
||||
var roots []gopathwalk.Root
|
||||
roots = append(roots, gopathwalk.Root{filepath.Join(goenv["GOROOT"], "src"), gopathwalk.RootGOROOT})
|
||||
roots = append(roots, gopathwalk.Root{Path: filepath.Join(goenv["GOROOT"], "src"), Type: gopathwalk.RootGOROOT})
|
||||
for _, p := range filepath.SplitList(goenv["GOPATH"]) {
|
||||
roots = append(roots, gopathwalk.Root{filepath.Join(p, "src"), gopathwalk.RootGOPATH})
|
||||
roots = append(roots, gopathwalk.Root{Path: filepath.Join(p, "src"), Type: gopathwalk.RootGOPATH})
|
||||
}
|
||||
// The callback is not necessarily safe to use in the goroutine below. Process roots eagerly.
|
||||
roots = filterRoots(roots, callback.rootFound)
|
||||
|
|
|
|||
22
vendor/golang.org/x/tools/internal/imports/mod.go
generated
vendored
22
vendor/golang.org/x/tools/internal/imports/mod.go
generated
vendored
|
|
@ -129,22 +129,22 @@ func (r *ModuleResolver) init() error {
|
|||
})
|
||||
|
||||
r.roots = []gopathwalk.Root{
|
||||
{filepath.Join(goenv["GOROOT"], "/src"), gopathwalk.RootGOROOT},
|
||||
{Path: filepath.Join(goenv["GOROOT"], "/src"), Type: gopathwalk.RootGOROOT},
|
||||
}
|
||||
r.mainByDir = make(map[string]*gocommand.ModuleJSON)
|
||||
for _, main := range r.mains {
|
||||
r.roots = append(r.roots, gopathwalk.Root{main.Dir, gopathwalk.RootCurrentModule})
|
||||
r.roots = append(r.roots, gopathwalk.Root{Path: main.Dir, Type: gopathwalk.RootCurrentModule})
|
||||
r.mainByDir[main.Dir] = main
|
||||
}
|
||||
if vendorEnabled {
|
||||
r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
|
||||
r.roots = append(r.roots, gopathwalk.Root{Path: r.dummyVendorMod.Dir, Type: gopathwalk.RootOther})
|
||||
} else {
|
||||
addDep := func(mod *gocommand.ModuleJSON) {
|
||||
if mod.Replace == nil {
|
||||
// This is redundant with the cache, but we'll skip it cheaply enough.
|
||||
r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootModuleCache})
|
||||
r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootModuleCache})
|
||||
} else {
|
||||
r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
|
||||
r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootOther})
|
||||
}
|
||||
}
|
||||
// Walk dependent modules before scanning the full mod cache, direct deps first.
|
||||
|
|
@ -158,7 +158,7 @@ func (r *ModuleResolver) init() error {
|
|||
addDep(mod)
|
||||
}
|
||||
}
|
||||
r.roots = append(r.roots, gopathwalk.Root{r.moduleCacheDir, gopathwalk.RootModuleCache})
|
||||
r.roots = append(r.roots, gopathwalk.Root{Path: r.moduleCacheDir, Type: gopathwalk.RootModuleCache})
|
||||
}
|
||||
|
||||
r.scannedRoots = map[gopathwalk.Root]bool{}
|
||||
|
|
@ -466,6 +466,16 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
|
|||
// We assume cached directories are fully cached, including all their
|
||||
// children, and have not changed. We can skip them.
|
||||
skip := func(root gopathwalk.Root, dir string) bool {
|
||||
if r.env.SkipPathInScan != nil && root.Type == gopathwalk.RootCurrentModule {
|
||||
if root.Path == dir {
|
||||
return false
|
||||
}
|
||||
|
||||
if r.env.SkipPathInScan(filepath.Clean(dir)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
info, ok := r.cacheLoad(dir)
|
||||
if !ok {
|
||||
return false
|
||||
|
|
|
|||
418
vendor/golang.org/x/tools/internal/imports/zstdlib.go
generated
vendored
418
vendor/golang.org/x/tools/internal/imports/zstdlib.go
generated
vendored
File diff suppressed because it is too large
Load diff
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
|
|
@ -743,7 +743,11 @@ golang.org/x/crypto/pkcs12/internal/rc2
|
|||
golang.org/x/crypto/salsa20/salsa
|
||||
golang.org/x/crypto/scrypt
|
||||
golang.org/x/crypto/sha3
|
||||
# golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
# golang.org/x/exp v0.0.0-20230307190834-24139beb5833
|
||||
## explicit; go 1.18
|
||||
golang.org/x/exp/constraints
|
||||
golang.org/x/exp/slices
|
||||
# golang.org/x/mod v0.6.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/mod/internal/lazyregexp
|
||||
golang.org/x/mod/module
|
||||
|
|
@ -806,7 +810,7 @@ golang.org/x/text/width
|
|||
# golang.org/x/time v0.2.0
|
||||
## explicit
|
||||
golang.org/x/time/rate
|
||||
# golang.org/x/tools v0.1.12
|
||||
# golang.org/x/tools v0.2.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
golang.org/x/tools/imports
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue