From 951f7653fc344ecf5787d7faf027879742ca3c75 Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Mon, 10 Aug 2026 09:45:49 +0500 Subject: [PATCH 01/44] docs: mention build cache in docker system prune description The synopsis only listed containers, networks, images, and volumes, but the command always prunes unused build cache (including BuildKit cache mounts). Align the description with the interactive warning. Signed-off-by: Dean Chen <862469039@qq.com> --- docs/reference/commandline/system_prune.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/reference/commandline/system_prune.md b/docs/reference/commandline/system_prune.md index 8d3805beae..ebacf0e998 100644 --- a/docs/reference/commandline/system_prune.md +++ b/docs/reference/commandline/system_prune.md @@ -18,7 +18,13 @@ Remove unused data ## Description Remove all unused containers, networks, images (both dangling and unused), -and optionally, volumes. +unused [build cache](https://docs.docker.com/build/cache/), and optionally, +volumes. + +Build cache is always eligible for pruning with this command (including BuildKit +cache mounts created with `RUN --mount=type=cache`). Use +[`docker builder prune`](builder_prune.md) if you only want to reclaim build +cache without removing containers, networks, or images. ## Examples From 3c53edb6c5c7ddc993df5ee49a046175093f353d Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Sat, 15 Aug 2026 19:38:59 +0500 Subject: [PATCH 02/44] docs(service create): note global mode still honors constraints A global service is one task per matching node, not one per node in the swarm. Say that next to --mode and --constraint. Signed-off-by: Dean Chen <862469039@qq.com> --- docs/reference/commandline/service_create.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/reference/commandline/service_create.md b/docs/reference/commandline/service_create.md index ddb6a3a083..0896c6a294 100644 --- a/docs/reference/commandline/service_create.md +++ b/docs/reference/commandline/service_create.md @@ -676,7 +676,9 @@ $ docker service create \ The service mode determines whether this is a _replicated_ service or a _global_ service. A replicated service runs as many tasks as specified, while a global -service runs on each active node in the swarm. +service runs one task on each active node in the swarm. Placement +`--constraint` expressions still apply: a global service only schedules +that task on nodes that match, not on every node. The following command creates a global service: @@ -690,7 +692,8 @@ $ docker service create \ ### Specify service constraints (--constraint) You can limit the set of nodes where a task can be scheduled by defining -constraint expressions. Constraint expressions can either use a _match_ (`==`) +constraint expressions. They apply to both replicated and global services +(and to jobs). Constraint expressions can either use a _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find nodes that satisfy every expression (AND match). Constraints can match node or Docker Engine labels as follows: From 4f169946e787270c73560c6532ee0442c0e86ee9 Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Tue, 18 Aug 2026 16:44:38 +0500 Subject: [PATCH 03/44] docs(history): use the pulled platform in the --platform example The example pulled linux/riscv64 then ran history for linux/s390x. Signed-off-by: Dean Chen <862469039@qq.com> --- docs/reference/commandline/image_history.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/commandline/image_history.md b/docs/reference/commandline/image_history.md index 1412dbdd45..e4b8264292 100644 --- a/docs/reference/commandline/image_history.md +++ b/docs/reference/commandline/image_history.md @@ -102,7 +102,7 @@ and shows its history. $ docker image pull --quiet --platform=linux/riscv64 alpine docker.io/library/alpine:latest -$ docker image history --platform=linux/s390x alpine +$ docker image history --platform=linux/riscv64 alpine IMAGE CREATED CREATED BY SIZE COMMENT beefdbd8a1da 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B 3 weeks ago /bin/sh -c #(nop) ADD file:ba2637314e600db5a… 8.46MB From a79a16bd63b83b17ec63b5b31893988bdd1664f8 Mon Sep 17 00:00:00 2001 From: Kamyar mofakhami <41609894+kawmy@users.noreply.github.com> Date: Fri, 21 Aug 2026 18:45:23 +0330 Subject: [PATCH 04/44] cli/command/service: preserve mount order on force update Signed-off-by: Kamyar mofakhami <41609894+kawmy@users.noreply.github.com> --- cli/command/service/update.go | 6 ++++-- cli/command/service/update_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 13af4bd746..5b0df486e9 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -352,8 +352,10 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags if err := updateIsolation(flagIsolation, &cspec.Isolation); err != nil { return err } - if err := updateMounts(flags, &cspec.Mounts); err != nil { - return err + if anyChanged(flags, flagMountAdd, flagMountRemove) { + if err := updateMounts(flags, &cspec.Mounts); err != nil { + return err + } } updateSysCtls(flags, &task.ContainerSpec.Sysctls) diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index 6158820951..38dd29cf9a 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -250,6 +250,32 @@ func TestUpdateMounts(t *testing.T) { assert.Check(t, is.Equal("/tokeep", mounts[1].Target)) } +func TestUpdateServiceForcePreservesMountOrder(t *testing.T) { + flags := newUpdateCommand(nil).Flags() + assert.NilError(t, flags.Set("force", "true")) + + spec := &swarm.ServiceSpec{ + TaskTemplate: swarm.TaskSpec{ + ContainerSpec: &swarm.ContainerSpec{ + Mounts: []mount.Mount{ + {Type: mount.TypeVolume, Source: "z-volume", Target: "/data/z"}, + {Type: mount.TypeVolume, Source: "a-volume", Target: "/data/a"}, + {Type: mount.TypeVolume, Source: "m-volume", Target: "/data/m"}, + }, + }, + }, + } + + err := updateService(context.Background(), nil, flags, spec) + assert.NilError(t, err) + assert.Equal(t, spec.TaskTemplate.ForceUpdate, uint64(1)) + assert.DeepEqual(t, spec.TaskTemplate.ContainerSpec.Mounts, []mount.Mount{ + {Type: mount.TypeVolume, Source: "z-volume", Target: "/data/z"}, + {Type: mount.TypeVolume, Source: "a-volume", Target: "/data/a"}, + {Type: mount.TypeVolume, Source: "m-volume", Target: "/data/m"}, + }) +} + func TestUpdateMountsWithDuplicateMounts(t *testing.T) { flags := newUpdateCommand(nil).Flags() flags.Set("mount-add", "type=volume,source=vol4,target=/toadd") From 5e0fdb3f6ecd1b763a7e2a43cd0169213638d58e Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Fri, 28 Aug 2026 14:53:19 +0500 Subject: [PATCH 05/44] registry: treat basic-auth 401 as unauthorized Client-side login (daemon down) was wrapping a 401 as a plain error, so Auth() moved on to the next endpoint. That can print Login Succeeded if the HTTP fallback happens to answer 200. Fixes #7237 Signed-off-by: Dean Chen <862469039@qq.com> --- internal/registry/auth.go | 6 ++++- internal/registry/auth_test.go | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 internal/registry/auth_test.go diff --git a/internal/registry/auth.go b/internal/registry/auth.go index e15a139cb4..1c0ccb6759 100644 --- a/internal/registry/auth.go +++ b/internal/registry/auth.go @@ -67,7 +67,11 @@ func loginV2(ctx context.Context, authConfig *registry.AuthConfig, endpoint APIE if resp.StatusCode != http.StatusOK { // TODO(dmcgowan): Attempt to further interpret result, status code and error code string - return "", fmt.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) + err := fmt.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) + if resp.StatusCode == http.StatusUnauthorized { + return "", unauthorizedErr{err} + } + return "", err } return credentialAuthConfig.IdentityToken, nil diff --git a/internal/registry/auth_test.go b/internal/registry/auth_test.go new file mode 100644 index 0000000000..4b7f1a8d5b --- /dev/null +++ b/internal/registry/auth_test.go @@ -0,0 +1,40 @@ +package registry + +import ( + "context" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/containerd/errdefs" + "github.com/moby/moby/api/types/registry" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestLoginV2BasicAuthUnauthorized(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, pass, ok := r.BasicAuth() + if !ok || user != "alice" || pass != "secret" { + w.Header().Set("WWW-Authenticate", `Basic realm="test"`) + http.Error(w, "401 Unauthorized", http.StatusUnauthorized) + return + } + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + + u, err := url.Parse(srv.URL) + assert.NilError(t, err) + endpoint := APIEndpoint{URL: u} + ctx := context.Background() + + _, err = loginV2(ctx, ®istry.AuthConfig{Username: "alice", Password: "wrong"}, endpoint, "docker-test") + assert.ErrorContains(t, err, "401") + assert.Check(t, errdefs.IsUnauthorized(err)) + + token, err := loginV2(ctx, ®istry.AuthConfig{Username: "alice", Password: "secret"}, endpoint, "docker-test") + assert.NilError(t, err) + assert.Check(t, is.Equal("", token)) +} From 0f0e561a3fd49091575fd9281f7d2e668d3a4532 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 28 Aug 2026 20:31:43 +0200 Subject: [PATCH 06/44] vendor: github.com/docker/docker-credential-helpers v0.9.9 full diff: https://github.com/docker/docker-credential-helpers/compare/v0.9.8...v0.9.9 Signed-off-by: Sebastiaan van Stijn --- vendor.mod | 2 +- vendor.sum | 4 ++-- vendor/modules.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor.mod b/vendor.mod index 0a21b03055..13c315a5e5 100644 --- a/vendor.mod +++ b/vendor.mod @@ -20,7 +20,7 @@ require ( github.com/distribution/reference v0.6.0 github.com/docker/cli-docs-tool v0.11.0 github.com/docker/distribution v2.8.3+incompatible - github.com/docker/docker-credential-helpers v0.9.8 + github.com/docker/docker-credential-helpers v0.9.9 github.com/docker/go-connections v0.8.1 github.com/docker/go-units v0.5.0 github.com/fvbommel/sortorder v1.1.0 diff --git a/vendor.sum b/vendor.sum index 9332aca82a..0c31bceb48 100644 --- a/vendor.sum +++ b/vendor.sum @@ -35,8 +35,8 @@ github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQt github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q= -github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/docker-credential-helpers v0.9.9 h1:BkydjIgZ46JnDbqyM2p2fc63KMw6y+KHL3Em/2AGJ7w= +github.com/docker/docker-credential-helpers v0.9.9/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M= github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 h1:Bj+mzWc7MJqqD0UzTaPmwszW3ttOVjSFi84ZU5l+2I0= diff --git a/vendor/modules.txt b/vendor/modules.txt index 1cebccd619..56d6c6dc08 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -68,7 +68,7 @@ github.com/docker/distribution/registry/client/auth/challenge github.com/docker/distribution/registry/client/transport github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory -# github.com/docker/docker-credential-helpers v0.9.8 +# github.com/docker/docker-credential-helpers v0.9.9 ## explicit; go 1.21 github.com/docker/docker-credential-helpers/client github.com/docker/docker-credential-helpers/credentials From 65b90d88884f67bab4c1e87eff1b1a337fe9daa6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 28 Aug 2026 20:33:30 +0200 Subject: [PATCH 07/44] vendor: github.com/docker/go-events v0.1.0 Signed-off-by: Sebastiaan van Stijn --- vendor.mod | 2 +- vendor.sum | 4 +- vendor/github.com/docker/go-events/README.md | 5 +- .../github.com/docker/go-events/broadcast.go | 53 ++++++++----------- vendor/github.com/docker/go-events/channel.go | 14 +++-- vendor/github.com/docker/go-events/event.go | 2 +- vendor/github.com/docker/go-events/filter.go | 21 +++++--- vendor/github.com/docker/go-events/queue.go | 1 + vendor/github.com/docker/go-events/retry.go | 37 ++++++------- vendor/github.com/docker/go-events/vendor.mod | 5 -- vendor/github.com/docker/go-events/vendor.sum | 16 ------ vendor/modules.txt | 4 +- 12 files changed, 68 insertions(+), 96 deletions(-) delete mode 100644 vendor/github.com/docker/go-events/vendor.mod delete mode 100644 vendor/github.com/docker/go-events/vendor.sum diff --git a/vendor.mod b/vendor.mod index 0a21b03055..54b4a0a53c 100644 --- a/vendor.mod +++ b/vendor.mod @@ -77,7 +77,7 @@ require ( github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect - github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 // indirect + github.com/docker/go-events v0.1.0 // indirect github.com/docker/go-metrics v0.1.0 // indirect github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/felixge/httpsnoop v1.1.0 // indirect diff --git a/vendor.sum b/vendor.sum index 9332aca82a..127859a2f9 100644 --- a/vendor.sum +++ b/vendor.sum @@ -39,8 +39,8 @@ github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEj github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M= github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= -github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 h1:Bj+mzWc7MJqqD0UzTaPmwszW3ttOVjSFi84ZU5l+2I0= -github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.1.0 h1:J8VX4H7Ta8mmBhv/K/24x1kosKOrEEnYmGjcdFHTOW0= +github.com/docker/go-events v0.1.0/go.mod h1:jwVwMgySJX4PiukIIRrind1wPGU8QhPa8aTEPkreUj4= github.com/docker/go-metrics v0.1.0 h1:r76KPNpstz+IvQKSWpYegSkkyzex0V3A1ZGVx6bhGlY= github.com/docker/go-metrics v0.1.0/go.mod h1:PciI3sONtB051kXALN1JoIlpcu54E1FuPh+4DuqEzyw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= diff --git a/vendor/github.com/docker/go-events/README.md b/vendor/github.com/docker/go-events/README.md index 410f61cf74..11e267f354 100644 --- a/vendor/github.com/docker/go-events/README.md +++ b/vendor/github.com/docker/go-events/README.md @@ -1,8 +1,7 @@ # Docker Events Package -[![GoDoc](https://godoc.org/github.com/docker/go-events?status.svg)](https://godoc.org/github.com/docker/go-events) -[![ci](https://github.com/docker/go-events/actions/workflows/ci.yml/badge.svg)](https://github.com/docker/go-events/actions/workflows/ci.yml) -[![Go Report Card](https://goreportcard.com/badge/github.com/docker/go-events)](https://goreportcard.com/report/github.com/docker/go-events) +[![Go Reference](https://pkg.go.dev/badge/github.com/docker/go-events.svg)](https://pkg.go.dev/github.com/docker/go-events) +[![Build Status](https://github.com/docker/go-events/actions/workflows/ci.yml/badge.svg)](https://github.com/docker/go-events/actions/workflows/ci.yml) The Docker `events` package implements a composable event distribution package for Go. diff --git a/vendor/github.com/docker/go-events/broadcast.go b/vendor/github.com/docker/go-events/broadcast.go index c81f600acc..ef8dd3f034 100644 --- a/vendor/github.com/docker/go-events/broadcast.go +++ b/vendor/github.com/docker/go-events/broadcast.go @@ -2,6 +2,7 @@ package events import ( "fmt" + "slices" "sync" "github.com/sirupsen/logrus" @@ -105,55 +106,46 @@ func (b *Broadcaster) Close() error { // Close is called, this goroutine will exit. func (b *Broadcaster) run() { defer close(b.closed) - remove := func(target Sink) { - for i, sink := range b.sinks { - if sink == target { - b.sinks = append(b.sinks[:i], b.sinks[i+1:]...) - break - } - } - } for { select { case event := <-b.events: - for _, sink := range b.sinks { + for i := 0; i < len(b.sinks); { + sink := b.sinks[i] if err := sink.Write(event); err != nil { if err == ErrSinkClosed { - // remove closed sinks - remove(sink) + b.sinks = slices.Delete(b.sinks, i, i+1) continue } - logrus.WithField("event", event).WithField("events.sink", sink).WithError(err). - Errorf("broadcaster: dropping event") + logrus.WithFields(logrus.Fields{ + "error": err, + "event": event, + "events.sink": sink, + }).Error("broadcaster: dropping event") } + i++ } case request := <-b.adds: // while we have to iterate for add/remove, common iteration for // send is faster against slice. - - var found bool - for _, sink := range b.sinks { - if request.sink == sink { - found = true - break - } - } - - if !found { + // b.sinks[request.sink] = struct{}{} + if !slices.Contains(b.sinks, request.sink) { b.sinks = append(b.sinks, request.sink) } - // b.sinks[request.sink] = struct{}{} request.response <- nil case request := <-b.removes: - remove(request.sink) + if i := slices.Index(b.sinks, request.sink); i >= 0 { + b.sinks = slices.Delete(b.sinks, i, i+1) + } request.response <- nil case <-b.shutdown: // close all the underlying sinks for _, sink := range b.sinks { if err := sink.Close(); err != nil && err != ErrSinkClosed { - logrus.WithField("events.sink", sink).WithError(err). - Errorf("broadcaster: closing sink failed") + logrus.WithFields(logrus.Fields{ + "error": err, + "events.sink": sink, + }).Error("broadcaster: closing sink failed") } } return @@ -164,8 +156,7 @@ func (b *Broadcaster) run() { func (b *Broadcaster) String() string { // Serialize copy of this broadcaster without the sync.Once, to avoid // a data race. - - b2 := map[string]interface{}{ + return fmt.Sprint(map[string]any{ "sinks": b.sinks, "events": b.events, "adds": b.adds, @@ -173,7 +164,5 @@ func (b *Broadcaster) String() string { "shutdown": b.shutdown, "closed": b.closed, - } - - return fmt.Sprint(b2) + }) } diff --git a/vendor/github.com/docker/go-events/channel.go b/vendor/github.com/docker/go-events/channel.go index 802cf51ffe..c58a2c3f57 100644 --- a/vendor/github.com/docker/go-events/channel.go +++ b/vendor/github.com/docker/go-events/channel.go @@ -34,10 +34,15 @@ func (ch *Channel) Done() chan struct{} { // the listener. func (ch *Channel) Write(event Event) error { select { - case ch.C <- event: - return nil case <-ch.closed: return ErrSinkClosed + default: + select { + case <-ch.closed: + return ErrSinkClosed + case ch.C <- event: + return nil + } } } @@ -53,9 +58,8 @@ func (ch *Channel) Close() error { func (ch *Channel) String() string { // Serialize a copy of the Channel that doesn't contain the sync.Once, // to avoid a data race. - ch2 := map[string]interface{}{ + return fmt.Sprint(map[string]any{ "C": ch.C, "closed": ch.closed, - } - return fmt.Sprint(ch2) + }) } diff --git a/vendor/github.com/docker/go-events/event.go b/vendor/github.com/docker/go-events/event.go index f0f1d9ea5f..8cd997a310 100644 --- a/vendor/github.com/docker/go-events/event.go +++ b/vendor/github.com/docker/go-events/event.go @@ -1,7 +1,7 @@ package events // Event marks items that can be sent as events. -type Event interface{} +type Event any // Sink accepts and sends events. type Sink interface { diff --git a/vendor/github.com/docker/go-events/filter.go b/vendor/github.com/docker/go-events/filter.go index e6c0eb69dd..5cf863029c 100644 --- a/vendor/github.com/docker/go-events/filter.go +++ b/vendor/github.com/docker/go-events/filter.go @@ -13,22 +13,27 @@ func (fn MatcherFunc) Match(event Event) bool { return fn(event) } -// Filter provides an event sink that sends only events that are accepted by a -// Matcher. No methods on filter are goroutine safe. -type Filter struct { +// Filter is the concrete implementation returned by [NewFilter]. +// +// Deprecated: Filter should not be constructed directly. Use [NewFilter] instead. +type Filter = filter + +type filter struct { dst Sink matcher Matcher closed bool } -// NewFilter returns a new filter that will send to events to dst that return -// true for Matcher. +// NewFilter returns a new event sink that forwards only events accepted by +// matcher to dst. +// +// The returned Sink's methods are not safe for concurrent use. func NewFilter(dst Sink, matcher Matcher) Sink { - return &Filter{dst: dst, matcher: matcher} + return &filter{dst: dst, matcher: matcher} } // Write an event to the filter. -func (f *Filter) Write(event Event) error { +func (f *filter) Write(event Event) error { if f.closed { return ErrSinkClosed } @@ -41,7 +46,7 @@ func (f *Filter) Write(event Event) error { } // Close the filter and allow no more events to pass through. -func (f *Filter) Close() error { +func (f *filter) Close() error { // TODO(stevvooe): Not all sinks should have Close. if f.closed { return nil diff --git a/vendor/github.com/docker/go-events/queue.go b/vendor/github.com/docker/go-events/queue.go index 4bb770afc2..b286405009 100644 --- a/vendor/github.com/docker/go-events/queue.go +++ b/vendor/github.com/docker/go-events/queue.go @@ -103,6 +103,7 @@ func (eq *Queue) next() Event { eq.cond.Wait() } + // Len is non-zero while holding eq.mu, so Front cannot be nil. front := eq.events.Front() block := front.Value.(Event) eq.events.Remove(front) diff --git a/vendor/github.com/docker/go-events/retry.go b/vendor/github.com/docker/go-events/retry.go index 9f75e230bb..70fad9c47c 100644 --- a/vendor/github.com/docker/go-events/retry.go +++ b/vendor/github.com/docker/go-events/retry.go @@ -2,7 +2,7 @@ package events import ( "fmt" - "math/rand" + "math/rand/v2" "sync" "sync/atomic" "time" @@ -26,13 +26,11 @@ type RetryingSink struct { // off on failure. Parameters threshold and backoff adjust the behavior of the // circuit breaker. func NewRetryingSink(sink Sink, strategy RetryStrategy) *RetryingSink { - rs := &RetryingSink{ + return &RetryingSink{ sink: sink, strategy: strategy, closed: make(chan struct{}), } - - return rs } // Write attempts to flush the events to the downstream sink until it succeeds @@ -66,14 +64,12 @@ retry: return err } - logger := logger.WithError(err) // shadow!! - if rs.strategy.Failure(event, err) { - logger.Errorf("retryingsink: dropped event") + logger.WithError(err).Error("retryingsink: dropped event") return nil } - logger.Errorf("retryingsink: error writing event, retrying") + logger.WithError(err).Error("retryingsink: error writing event, retrying") goto retry } @@ -93,7 +89,7 @@ func (rs *RetryingSink) Close() error { func (rs *RetryingSink) String() string { // Serialize a copy of the RetryingSink without the sync.Once, to avoid // a data race. - rs2 := map[string]interface{}{ + rs2 := map[string]any{ "sink": rs.sink, "strategy": rs.strategy, "closed": rs.closed, @@ -201,7 +197,7 @@ type ExponentialBackoffConfig struct { // ExponentialBackoff implements random backoff with exponentially increasing // bounds as the number consecutive failures increase. type ExponentialBackoff struct { - failures uint64 // consecutive failure counter (needs to be 64-bit aligned) + failures atomic.Uint64 // consecutive failure counter (needs to be 64-bit aligned) config ExponentialBackoffConfig } @@ -215,17 +211,17 @@ func NewExponentialBackoff(config ExponentialBackoffConfig) *ExponentialBackoff // Proceed returns the next randomly bound exponential backoff time. func (b *ExponentialBackoff) Proceed(event Event) time.Duration { - return b.backoff(atomic.LoadUint64(&b.failures)) + return b.backoff(b.failures.Load()) } // Success resets the failures counter. func (b *ExponentialBackoff) Success(event Event) { - atomic.StoreUint64(&b.failures, 0) + b.failures.Store(0) } // Failure increments the failure counter. func (b *ExponentialBackoff) Failure(event Event, err error) bool { - atomic.AddUint64(&b.failures, 1) + b.failures.Add(1) return false } @@ -242,17 +238,16 @@ func (b *ExponentialBackoff) backoff(failures uint64) time.Duration { factor = DefaultExponentialBackoffConfig.Factor } - backoff := b.config.Base + factor*time.Duration(1<<(failures-1)) - - max := b.config.Max - if max <= 0 { - max = DefaultExponentialBackoffConfig.Max + maxBackoff := b.config.Max + if maxBackoff <= 0 { + maxBackoff = DefaultExponentialBackoffConfig.Max } - if backoff > max || backoff < 0 { - backoff = max + backoff := b.config.Base + factor*time.Duration(1<<(failures-1)) + if backoff > maxBackoff || backoff < 0 { + backoff = maxBackoff } // Choose a uniformly distributed value from [0, backoff). - return time.Duration(rand.Int63n(int64(backoff))) + return rand.N(backoff) } diff --git a/vendor/github.com/docker/go-events/vendor.mod b/vendor/github.com/docker/go-events/vendor.mod deleted file mode 100644 index 66a2cd9e5c..0000000000 --- a/vendor/github.com/docker/go-events/vendor.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/docker/go-events - -go 1.13 - -require github.com/sirupsen/logrus v1.9.3 diff --git a/vendor/github.com/docker/go-events/vendor.sum b/vendor/github.com/docker/go-events/vendor.sum deleted file mode 100644 index 9243c28735..0000000000 --- a/vendor/github.com/docker/go-events/vendor.sum +++ /dev/null @@ -1,16 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/modules.txt b/vendor/modules.txt index 1cebccd619..4a81d85af4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -77,8 +77,8 @@ github.com/docker/docker-credential-helpers/credentials github.com/docker/go-connections/nat github.com/docker/go-connections/sockets github.com/docker/go-connections/tlsconfig -# github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 -## explicit +# github.com/docker/go-events v0.1.0 +## explicit; go 1.24 github.com/docker/go-events # github.com/docker/go-metrics v0.1.0 ## explicit; go 1.21 From 76b505b05468084e961f92135f08631709ae8ae9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 28 Aug 2026 20:36:33 +0200 Subject: [PATCH 08/44] vendor: github.com/klauspost/compress v1.19.2 full diff: https://github.com/klauspost/compress/compare/v1.18.7...v1.19.2 Signed-off-by: Sebastiaan van Stijn --- vendor.mod | 2 +- vendor.sum | 4 +- .../github.com/klauspost/compress/README.md | 12 + .../klauspost/compress/huff0/build_table.go | 168 + .../compress/huff0/decompress_amd64.go | 203 +- .../compress/huff0/decompress_amd64.s | 4 +- .../compress/huff0/decompress_arm64.go | 37 + .../compress/huff0/decompress_arm64.s | 851 +++++ .../compress/huff0/decompress_asm.go | 193 ++ .../compress/huff0/decompress_generic.go | 2 +- .../compress/internal/snapref/decode.go | 2 +- .../klauspost/compress/zstd/README.md | 37 +- .../klauspost/compress/zstd/blockdec.go | 9 +- .../klauspost/compress/zstd/dict.go | 88 +- .../klauspost/compress/zstd/enc_base.go | 28 + .../klauspost/compress/zstd/enc_best.go | 15 + .../klauspost/compress/zstd/enc_better.go | 18 + .../klauspost/compress/zstd/enc_dfast.go | 16 + .../klauspost/compress/zstd/enc_fast.go | 17 + .../klauspost/compress/zstd/enc_jobs.go | 352 +++ .../klauspost/compress/zstd/encoder.go | 210 +- .../compress/zstd/encoder_options.go | 69 +- .../compress/zstd/fse_decoder_amd64.s | 2 +- .../compress/zstd/fse_decoder_arm64.s | 153 + ...se_decoder_amd64.go => fse_decoder_asm.go} | 8 +- .../compress/zstd/fse_decoder_generic.go | 2 +- .../klauspost/compress/zstd/seqdec_amd64.go | 384 +-- .../klauspost/compress/zstd/seqdec_amd64.s | 6 +- .../klauspost/compress/zstd/seqdec_arm64.go | 70 + .../klauspost/compress/zstd/seqdec_arm64.s | 2750 +++++++++++++++++ .../klauspost/compress/zstd/seqdec_asm.go | 316 ++ .../klauspost/compress/zstd/seqdec_generic.go | 2 +- .../klauspost/compress/zstd/snappy.go | 7 +- vendor/modules.txt | 2 +- 34 files changed, 5449 insertions(+), 590 deletions(-) create mode 100644 vendor/github.com/klauspost/compress/huff0/build_table.go create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress_arm64.go create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress_arm64.s create mode 100644 vendor/github.com/klauspost/compress/huff0/decompress_asm.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_jobs.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s rename vendor/github.com/klauspost/compress/zstd/{fse_decoder_amd64.go => fse_decoder_asm.go} (81%) create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_asm.go diff --git a/vendor.mod b/vendor.mod index 0a21b03055..e6209387e3 100644 --- a/vendor.mod +++ b/vendor.mod @@ -87,7 +87,7 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.18.7 // indirect + github.com/klauspost/compress v1.19.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/user v0.4.1 // indirect github.com/moby/sys/userns v0.2.0 // indirect diff --git a/vendor.sum b/vendor.sum index 9332aca82a..2294d48345 100644 --- a/vendor.sum +++ b/vendor.sum @@ -78,8 +78,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= -github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= +github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-runewidth v0.0.28 h1:rPyg2ybwEKPebvpzVWe1gKBkH8EQFkxO4Y0hjBeLaBU= diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index fb023f2cf2..0e9f170d01 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -27,6 +27,18 @@ Use the links above for more information on each. # changelog +* Jul 1st, 2026 [1.19.0](https://github.com/klauspost/compress/releases/tag/v1.19.0) + * zstd: Add true concurrent stream encodingin https://github.com/klauspost/compress/pull/1136 + * zstd: arm64 decoder asm by @lizthegrey in https://github.com/klauspost/compress/pull/1160 + * flate: Add inflate checkpoints in https://github.com/klauspost/compress/pull/1154 + * zstd: avoid unused BuildDict encoder allocation by @snissn in https://github.com/klauspost/compress/pull/1147 + * snappy/s2: Limit length of varint in `decodedLen` by @eustas in https://github.com/klauspost/compress/pull/1148 + * gzhttp: match qvalue parameter case-insensitively (RFC 7231) by @z9z in https://github.com/klauspost/compress/pull/1149 + * zip: add NameDecoder callback for legacy encoding rewrite by @SAY-5 in https://github.com/klauspost/compress/pull/1150 + * huff0: Allow building tables from histogram in https://github.com/klauspost/compress/pull/1155 + * huff0: Allow building table from oversized histogram in https://github.com/klauspost/compress/pull/1156 + * s2sx: Clean symlink targets in https://github.com/klauspost/compress/pull/1163 + * Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 diff --git a/vendor/github.com/klauspost/compress/huff0/build_table.go b/vendor/github.com/klauspost/compress/huff0/build_table.go new file mode 100644 index 0000000000..e3757c87e0 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/build_table.go @@ -0,0 +1,168 @@ +package huff0 + +import "errors" + +// BuildCTable builds a Huffman compression table from a precomputed symbol +// histogram and installs it as the previous (reuse) table on s. +// +// After this call: +// - EstimateSize/CanUseTable can probe the table against other histograms. +// - Compress1X/Compress4X with Reuse = ReusePolicyMust will encode without +// emitting a new table header. +// - TransferCTable can hand the table to a sibling Scratch. +// +// count[i] is the number of occurrences of symbol i. The histogram must have +// at least 2 distinct non-zero symbols; ErrUseRLE is returned for a single +// symbol and an error is returned for an empty histogram. +func (s *Scratch) BuildCTable(count *[256]uint32) error { + if s == nil { + return errors.New("huff0: BuildCTable on nil Scratch") + } + if count == nil { + return errors.New("huff0: nil count passed to BuildCTable") + } + var err error + s, err = s.prepare(nil) + if err != nil { + return err + } + s.count = *count + var total, maxCount int + var symLen uint16 + for i, v := range s.count { + total += int(v) + if int(v) > maxCount { + maxCount = int(v) + } + if v != 0 { + symLen = uint16(i) + 1 + } + } + if total == 0 { + return errors.New("huff0: empty histogram") + } + if symLen < 2 || maxCount == total { + return ErrUseRLE + } + // huff0's internal rank table assumes total ≤ BlockSizeMax (it uses + // highBit32(count+1) + 1 as a rank index into a fixed-size array). + // Histograms summed across multiple blocks can exceed that; scale the + // counts down preserving the distribution. Non-zero entries round up so + // rare symbols stay representable. + if total > BlockSizeMax { + shift := uint(0) + for total>>shift > BlockSizeMax { + shift++ + } + round := uint32(1<> shift + if scaled == 0 { + scaled = 1 + } + s.count[i] = scaled + newTotal += int(scaled) + if int(scaled) > newMax { + newMax = int(scaled) + } + } + total = newTotal + maxCount = newMax + if maxCount == total { + return ErrUseRLE + } + } + s.symbolLen = symLen + s.maxCount = maxCount + s.srcLen = total + if err := s.buildCTable(); err != nil { + return err + } + if cap(s.prevTable) < len(s.cTable) { + s.prevTable = make(cTable, 0, maxSymbolValue+1) + } + s.prevTable = s.prevTable[:len(s.cTable)] + copy(s.prevTable, s.cTable) + s.prevTableLog = s.actualTableLog + // Force the next Compress* to recount from real input. + s.clearCount = true + s.maxCount = 0 + return nil +} + +// EstimateSize returns an estimated compressed payload size in bytes for the +// supplied histogram using the table currently stored in prevTable. It returns +// -1 when the table cannot encode every non-zero symbol of hist (i.e. when +// CanUseTable would return false). The estimate excludes the table header. +func (s *Scratch) EstimateSize(hist *[256]uint32) int { + if s == nil || hist == nil || len(s.prevTable) == 0 { + return -1 + } + pt := s.prevTable + nbBits := uint32(7) + for i, v := range hist { + if v == 0 { + continue + } + if i >= len(pt) || pt[i].nBits == 0 { + return -1 + } + nbBits += uint32(pt[i].nBits) * v + } + return int(nbBits >> 3) +} + +// CanUseTable reports whether the table in prevTable can encode every +// non-zero symbol present in hist. +func (s *Scratch) CanUseTable(hist *[256]uint32) bool { + if s == nil || hist == nil || len(s.prevTable) == 0 { + return false + } + pt := s.prevTable + for i, v := range hist { + if v == 0 { + continue + } + if i >= len(pt) || pt[i].nBits == 0 { + return false + } + } + return true +} + +// AppendTable serializes the table currently stored in prevTable (e.g. as +// installed by BuildCTable or carried over from a previous Compress call) +// into a self-delimiting zstd-style header and appends it to dst. The +// returned slice can be parsed back by ReadTable. +func (s *Scratch) AppendTable(dst []byte) ([]byte, error) { + if s == nil || len(s.prevTable) == 0 { + return dst, errors.New("huff0: AppendTable with empty table") + } + // cTable.write reads s.actualTableLog, s.symbolLen, s.huffWeight, s.fse + // and writes into s.Out. Save/restore Out so we don't disturb in-flight + // compression buffers. + saveOut := s.Out + saveTL := s.actualTableLog + saveSL := s.symbolLen + if s.fse == nil { + // Lazily init in case AppendTable is called on a fresh Scratch. + if _, err := s.prepare(nil); err != nil { + return dst, err + } + saveOut = s.Out + } + s.Out = s.Out[:0] + s.actualTableLog = s.prevTableLog + s.symbolLen = uint16(len(s.prevTable)) + if err := s.prevTable.write(s); err != nil { + s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL + return dst, err + } + dst = append(dst, s.Out...) + s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL + return dst, nil +} diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go index 2d6ef64be1..7035d656d9 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go @@ -1,222 +1,49 @@ //go:build amd64 && !appengine && !noasm && gc -// This file contains the specialisation of Decoder.Decompress4X -// and Decoder.Decompress1X that use an asm implementation of thir main loops. +// amd64 stubs and dispatch for the asm loops used by decompress_asm.go. package huff0 import ( - "errors" - "fmt" - "github.com/klauspost/compress/internal/cpuinfo" ) -// decompress4x_main_loop_x86 is an x86 assembler implementation +// decompress4x_main_loop_amd64 is an x86 assembler implementation // of Decompress4X when tablelog > 8. // //go:noescape func decompress4x_main_loop_amd64(ctx *decompress4xContext) -// decompress4x_8b_loop_x86 is an x86 assembler implementation +// decompress4x_8b_main_loop_amd64 is an x86 assembler implementation // of Decompress4X when tablelog <= 8 which decodes 4 entries // per loop. // //go:noescape func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) -// fallback8BitSize is the size where using Go version is faster. -const fallback8BitSize = 800 - -type decompress4xContext struct { - pbr *[4]bitReaderShifted - peekBits uint8 - out *byte - dstEvery int - tbl *dEntrySingle - decoded int - limit *byte -} - -// Decompress4X will decompress a 4X encoded stream. -// The length of the supplied input must match the end of a block exactly. -// The *capacity* of the dst slice must match the destination size of -// the uncompressed data exactly. -func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { - if len(d.dt.single) == 0 { - return nil, errors.New("no table loaded") - } - if len(src) < 6+(4*1) { - return nil, errors.New("input too small") - } - - use8BitTables := d.actualTableLog <= 8 - if cap(dst) < fallback8BitSize && use8BitTables { - return d.decompress4X8bit(dst, src) - } - - var br [4]bitReaderShifted - // Decode "jump table" - start := 6 - for i := range 3 { - length := int(src[i*2]) | (int(src[i*2+1]) << 8) - if start+length >= len(src) { - return nil, errors.New("truncated input (or invalid offset)") - } - err := br[i].init(src[start : start+length]) - if err != nil { - return nil, err - } - start += length - } - err := br[3].init(src[start:]) - if err != nil { - return nil, err - } - - // destination, offset to match first output - dstSize := cap(dst) - dst = dst[:dstSize] - out := dst - dstEvery := (dstSize + 3) / 4 - - const tlSize = 1 << tableLogMax - const tlMask = tlSize - 1 - single := d.dt.single[:tlSize] - - var decoded int - - if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) { - ctx := decompress4xContext{ - pbr: &br, - peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() - out: &out[0], - dstEvery: dstEvery, - tbl: &single[0], - limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last. - } - if use8BitTables { - decompress4x_8b_main_loop_amd64(&ctx) - } else { - decompress4x_main_loop_amd64(&ctx) - } - - decoded = ctx.decoded - out = out[decoded/4:] - } - - // Decode remaining. - remainBytes := dstEvery - (decoded / 4) - for i := range br { - offset := dstEvery * i - endsAt := min(offset+remainBytes, len(out)) - br := &br[i] - bitsLeft := br.remaining() - for bitsLeft > 0 { - br.fill() - if offset >= endsAt { - return nil, errors.New("corruption detected: stream overrun 4") - } - - // Read value and increment offset. - val := br.peekBitsFast(d.actualTableLog) - v := single[val&tlMask].entry - nBits := uint8(v) - br.advance(nBits) - bitsLeft -= uint(nBits) - out[offset] = uint8(v >> 8) - offset++ - } - if offset != endsAt { - return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) - } - decoded += offset - dstEvery*i - err = br.close() - if err != nil { - return nil, err - } - } - if dstSize != decoded { - return nil, errors.New("corruption detected: short output block") - } - return dst, nil -} - -// decompress4x_main_loop_x86 is an x86 assembler implementation +// decompress1x_main_loop_amd64 is an x86 assembler implementation // of Decompress1X when tablelog > 8. // //go:noescape func decompress1x_main_loop_amd64(ctx *decompress1xContext) -// decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation +// decompress1x_main_loop_bmi2 is an x86 with BMI2 assembler implementation // of Decompress1X when tablelog > 8. // //go:noescape func decompress1x_main_loop_bmi2(ctx *decompress1xContext) -type decompress1xContext struct { - pbr *bitReaderShifted - peekBits uint8 - out *byte - outCap int - tbl *dEntrySingle - decoded int +func decompress4x_main_loop_asm(ctx *decompress4xContext) { + decompress4x_main_loop_amd64(ctx) } -// Error reported by asm implementations -const error_max_decoded_size_exeeded = -1 +func decompress4x_8b_main_loop_asm(ctx *decompress4xContext) { + decompress4x_8b_main_loop_amd64(ctx) +} -// Decompress1X will decompress a 1X encoded stream. -// The cap of the output buffer will be the maximum decompressed size. -// The length of the supplied input must match the end of a block exactly. -func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { - if len(d.dt.single) == 0 { - return nil, errors.New("no table loaded") +func decompress1x_main_loop_asm(ctx *decompress1xContext) { + if cpuinfo.HasBMI2() { + decompress1x_main_loop_bmi2(ctx) + } else { + decompress1x_main_loop_amd64(ctx) } - var br bitReaderShifted - err := br.init(src) - if err != nil { - return dst, err - } - maxDecodedSize := cap(dst) - dst = dst[:maxDecodedSize] - - const tlSize = 1 << tableLogMax - const tlMask = tlSize - 1 - - if maxDecodedSize >= 4 { - ctx := decompress1xContext{ - pbr: &br, - out: &dst[0], - outCap: maxDecodedSize, - peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() - tbl: &d.dt.single[0], - } - - if cpuinfo.HasBMI2() { - decompress1x_main_loop_bmi2(&ctx) - } else { - decompress1x_main_loop_amd64(&ctx) - } - if ctx.decoded == error_max_decoded_size_exeeded { - return nil, ErrMaxDecodedSizeExceeded - } - - dst = dst[:ctx.decoded] - } - - // br < 8, so uint8 is fine - bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead - for bitsLeft > 0 { - br.fill() - if len(dst) >= maxDecodedSize { - br.close() - return nil, ErrMaxDecodedSizeExceeded - } - v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] - nBits := uint8(v.entry) - br.advance(nBits) - bitsLeft -= nBits - dst = append(dst, uint8(v.entry>>8)) - } - return dst, br.close() } diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s index c4c7ab2d1f..c5d4a710a0 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s +++ b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.s @@ -1,6 +1,6 @@ -// Code generated by command: go run gen.go -out ../decompress_amd64.s -pkg=huff0. DO NOT EDIT. +// Code generated by command: go run gen.go -out ../decompress.s -arch amd64,arm64 -pkg=huff0. DO NOT EDIT. -//go:build amd64 && !appengine && !noasm && gc +//go:build !appengine && !noasm && gc // func decompress4x_main_loop_amd64(ctx *decompress4xContext) TEXT ·decompress4x_main_loop_amd64(SB), $0-8 diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_arm64.go b/vendor/github.com/klauspost/compress/huff0/decompress_arm64.go new file mode 100644 index 0000000000..8ba3c810e6 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress_arm64.go @@ -0,0 +1,37 @@ +//go:build arm64 && !appengine && !noasm && gc + +// arm64 stubs and dispatch for the asm loops used by decompress_asm.go. +// The asm (decompress_arm64.s) is generated by the avo arm64 lowering +// printer from the same source as the amd64 asm; see _generate/gen.go. +package huff0 + +// decompress4x_main_loop_arm64 is an arm64 assembler implementation +// of Decompress4X when tablelog > 8. +// +//go:noescape +func decompress4x_main_loop_arm64(ctx *decompress4xContext) + +// decompress4x_8b_main_loop_arm64 is an arm64 assembler implementation +// of Decompress4X when tablelog <= 8 which decodes 4 entries +// per loop. +// +//go:noescape +func decompress4x_8b_main_loop_arm64(ctx *decompress4xContext) + +// decompress1x_main_loop_arm64 is an arm64 assembler implementation +// of Decompress1X when tablelog > 8. +// +//go:noescape +func decompress1x_main_loop_arm64(ctx *decompress1xContext) + +func decompress4x_main_loop_asm(ctx *decompress4xContext) { + decompress4x_main_loop_arm64(ctx) +} + +func decompress4x_8b_main_loop_asm(ctx *decompress4xContext) { + decompress4x_8b_main_loop_arm64(ctx) +} + +func decompress1x_main_loop_asm(ctx *decompress1xContext) { + decompress1x_main_loop_arm64(ctx) +} diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s b/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s new file mode 100644 index 0000000000..e16012721a --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress_arm64.s @@ -0,0 +1,851 @@ +// Code generated by command: go run gen.go -out ../decompress.s -arch amd64,arm64 -pkg=huff0. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc + +// func decompress4x_main_loop_amd64(ctx *decompress4xContext) +TEXT ·decompress4x_main_loop_arm64(SB), $0-8 + // Preload values + MOVD ctx+0(FP), R0 + MOVBU 8(R0), R6 + MOVD 16(R0), R3 + MOVD 48(R0), R5 + MOVD 24(R0), R7 + MOVD 32(R0), R8 + MOVD (R0), R9 + + // Main loop +main_loop: + MOVD $0, R2 + CMP R5, R3 + CSET GE, R16 + BFI $0, R16, $8, R2 + + // br0.fillFast32() + MOVD 32(R9), R10 + MOVBU 40(R9), R11 + CMP $0x20, R11 + BLS skip_fill0 + MOVD 24(R9), R0 + SUB $0x20, R11, R11 + SUB $0x04, R0, R0 + MOVD (R9), R12 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R12, R0, R15 + MOVWU (R15), R12 + MOVD R11, R1 + LSL R1, R12, R12 + MOVD R0, 24(R9) + ORR R12, R10, R10 + + // exhausted += (br0.off < 4) + CMP $0x04, R0 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill0: + // val0 := br0.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br0.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br0.peekTopBits(peekBits) + MOVD R6, R1 + MOVD R10, R12 + LSR R1, R12, R12 + + // v1 := table[val1&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br0.advance(uint8(v1.entry)) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + MOVH R0, (R3) + + // update the bitreader structure + MOVD R10, 32(R9) + MOVB R11, 40(R9) + + // br1.fillFast32() + MOVD 80(R9), R10 + MOVBU 88(R9), R11 + CMP $0x20, R11 + BLS skip_fill1 + MOVD 72(R9), R0 + SUB $0x20, R11, R11 + SUB $0x04, R0, R0 + MOVD 48(R9), R12 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R12, R0, R15 + MOVWU (R15), R12 + MOVD R11, R1 + LSL R1, R12, R12 + MOVD R0, 72(R9) + ORR R12, R10, R10 + + // exhausted += (br1.off < 4) + CMP $0x04, R0 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill1: + // val0 := br1.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br1.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br1.peekTopBits(peekBits) + MOVD R6, R1 + MOVD R10, R12 + LSR R1, R12, R12 + + // v1 := table[val1&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br1.advance(uint8(v1.entry)) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + ADD R7, R3, R15 + MOVH R0, (R15) + + // update the bitreader structure + MOVD R10, 80(R9) + MOVB R11, 88(R9) + + // br2.fillFast32() + MOVD 128(R9), R10 + MOVBU 136(R9), R11 + CMP $0x20, R11 + BLS skip_fill2 + MOVD 120(R9), R0 + SUB $0x20, R11, R11 + SUB $0x04, R0, R0 + MOVD 96(R9), R12 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R12, R0, R15 + MOVWU (R15), R12 + MOVD R11, R1 + LSL R1, R12, R12 + MOVD R0, 120(R9) + ORR R12, R10, R10 + + // exhausted += (br2.off < 4) + CMP $0x04, R0 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill2: + // val0 := br2.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br2.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br2.peekTopBits(peekBits) + MOVD R6, R1 + MOVD R10, R12 + LSR R1, R12, R12 + + // v1 := table[val1&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br2.advance(uint8(v1.entry)) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + ADD R7<<1, R3, R15 + MOVH R0, (R15) + + // update the bitreader structure + MOVD R10, 128(R9) + MOVB R11, 136(R9) + + // br3.fillFast32() + MOVD 176(R9), R10 + MOVBU 184(R9), R11 + CMP $0x20, R11 + BLS skip_fill3 + MOVD 168(R9), R0 + SUB $0x20, R11, R11 + SUB $0x04, R0, R0 + MOVD 144(R9), R12 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R12, R0, R15 + MOVWU (R15), R12 + MOVD R11, R1 + LSL R1, R12, R12 + MOVD R0, 168(R9) + ORR R12, R10, R10 + + // exhausted += (br3.off < 4) + CMP $0x04, R0 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill3: + // val0 := br3.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br3.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br3.peekTopBits(peekBits) + MOVD R6, R1 + MOVD R10, R12 + LSR R1, R12, R12 + + // v1 := table[val1&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br3.advance(uint8(v1.entry)) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // these two writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + ADD R7<<1, R7, R1 + ADD R1, R3, R15 + MOVH R0, (R15) + + // update the bitreader structure + MOVD R10, 176(R9) + MOVB R11, 184(R9) + ADD $0x02, R3, R3 + AND $0xff, R2, R15 + AND $0xff, R2, R16 + TST R16, R15 + BEQ main_loop + MOVD ctx+0(FP), R0 + MOVD 16(R0), R16 + SUB R16, R3, R3 + LSL $0x02, R3, R3 + MOVD R3, 40(R0) + RET + +// func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) +TEXT ·decompress4x_8b_main_loop_arm64(SB), $0-8 + // Preload values + MOVD ctx+0(FP), R1 + MOVBU 8(R1), R6 + MOVD 16(R1), R3 + MOVD 48(R1), R5 + MOVD 24(R1), R7 + MOVD 32(R1), R8 + MOVD (R1), R9 + + // Main loop +main_loop: + MOVD $0, R2 + CMP R5, R3 + CSET GE, R16 + BFI $0, R16, $8, R2 + + // br0.fillFast32() + MOVD 32(R9), R10 + MOVBU 40(R9), R11 + CMP $0x20, R11 + BLS skip_fill0 + MOVD 24(R9), R12 + SUB $0x20, R11, R11 + SUB $0x04, R12, R12 + MOVD (R9), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R13, R12, R15 + MOVWU (R15), R13 + MOVD R11, R1 + LSL R1, R13, R13 + MOVD R12, 24(R9) + ORR R13, R10, R10 + + // exhausted += (br0.off < 4) + CMP $0x04, R12 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill0: + // val0 := br0.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br0.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br0.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v1 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br0.advance(uint8(v1.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // val2 := br0.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v2 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br0.advance(uint8(v2.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val3 := br0.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v3 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br0.advance(uint8(v3.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + MOVW R0, (R3) + + // update the bitreader structure + MOVD R10, 32(R9) + MOVB R11, 40(R9) + + // br1.fillFast32() + MOVD 80(R9), R10 + MOVBU 88(R9), R11 + CMP $0x20, R11 + BLS skip_fill1 + MOVD 72(R9), R12 + SUB $0x20, R11, R11 + SUB $0x04, R12, R12 + MOVD 48(R9), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R13, R12, R15 + MOVWU (R15), R13 + MOVD R11, R1 + LSL R1, R13, R13 + MOVD R12, 72(R9) + ORR R13, R10, R10 + + // exhausted += (br1.off < 4) + CMP $0x04, R12 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill1: + // val0 := br1.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br1.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br1.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v1 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br1.advance(uint8(v1.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // val2 := br1.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v2 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br1.advance(uint8(v2.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val3 := br1.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v3 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br1.advance(uint8(v3.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + ADD R7, R3, R15 + MOVW R0, (R15) + + // update the bitreader structure + MOVD R10, 80(R9) + MOVB R11, 88(R9) + + // br2.fillFast32() + MOVD 128(R9), R10 + MOVBU 136(R9), R11 + CMP $0x20, R11 + BLS skip_fill2 + MOVD 120(R9), R12 + SUB $0x20, R11, R11 + SUB $0x04, R12, R12 + MOVD 96(R9), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R13, R12, R15 + MOVWU (R15), R13 + MOVD R11, R1 + LSL R1, R13, R13 + MOVD R12, 120(R9) + ORR R13, R10, R10 + + // exhausted += (br2.off < 4) + CMP $0x04, R12 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill2: + // val0 := br2.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br2.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br2.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v1 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br2.advance(uint8(v1.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // val2 := br2.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v2 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br2.advance(uint8(v2.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val3 := br2.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v3 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br2.advance(uint8(v3.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + ADD R7<<1, R3, R15 + MOVW R0, (R15) + + // update the bitreader structure + MOVD R10, 128(R9) + MOVB R11, 136(R9) + + // br3.fillFast32() + MOVD 176(R9), R10 + MOVBU 184(R9), R11 + CMP $0x20, R11 + BLS skip_fill3 + MOVD 168(R9), R12 + SUB $0x20, R11, R11 + SUB $0x04, R12, R12 + MOVD 144(R9), R13 + + // b.value |= uint64(low) << (b.bitsRead & 63) + ADD R13, R12, R15 + MOVWU (R15), R13 + MOVD R11, R1 + LSL R1, R13, R13 + MOVD R12, 168(R9) + ORR R13, R10, R10 + + // exhausted += (br3.off < 4) + CMP $0x04, R12 + CSINC HS, R2, R2, R16 + BFI $0, R16, $8, R2 + +skip_fill3: + // val0 := br3.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v0 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br3.advance(uint8(v0.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val1 := br3.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v1 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br3.advance(uint8(v1.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // val2 := br3.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v2 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br3.advance(uint8(v2.entry) + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + + // val3 := br3.peekTopBits(peekBits) + MOVD R10, R12 + MOVD R6, R1 + LSR R1, R12, R12 + + // v3 := table[val0&mask] + ADD R12<<1, R8, R15 + MOVHU (R15), R1 + + // br3.advance(uint8(v3.entry) + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + LSL R1, R10, R10 + ADD R1, R11, R15 + BFI $0, R15, $8, R11 + REVW R0, R0 + + // these four writes get coalesced + // out[id * dstEvery + 0] = uint8(v0.entry >> 8) + // out[id * dstEvery + 1] = uint8(v1.entry >> 8) + // out[id * dstEvery + 3] = uint8(v2.entry >> 8) + // out[id * dstEvery + 4] = uint8(v3.entry >> 8) + ADD R7<<1, R7, R1 + ADD R1, R3, R15 + MOVW R0, (R15) + + // update the bitreader structure + MOVD R10, 176(R9) + MOVB R11, 184(R9) + ADD $0x04, R3, R3 + AND $0xff, R2, R15 + AND $0xff, R2, R16 + TST R16, R15 + BEQ main_loop + MOVD ctx+0(FP), R0 + MOVD 16(R0), R16 + SUB R16, R3, R3 + LSL $0x02, R3, R3 + MOVD R3, 40(R0) + RET + +// func decompress1x_main_loop_amd64(ctx *decompress1xContext) +TEXT ·decompress1x_main_loop_arm64(SB), $0-8 + MOVD ctx+0(FP), R1 + MOVD 16(R1), R2 + MOVD 24(R1), R3 + CMP $0x04, R3 + BLO error_max_decoded_size_exceeded + ADD R3, R2, R3 + MOVD (R1), R5 + MOVD (R5), R7 + MOVD 24(R5), R8 + MOVD 32(R5), R9 + MOVBU 40(R5), R10 + MOVD 32(R1), R5 + MOVBU 8(R1), R6 + JMP loop_condition + +main_loop: + // Check if we have room for 4 bytes in the output buffer + ADD $4, R2, R1 + CMP R3, R1 + BGE error_max_decoded_size_exceeded + + // Decode 4 values + CMP $0x20, R10 + BLT bitReader_fillFast_1_end + SUB $0x20, R10, R10 + SUB $0x04, R8, R8 + ADD R8, R7, R15 + MOVWU (R15), R11 + MOVD R10, R1 + LSL R1, R11, R11 + ORR R11, R9, R9 + +bitReader_fillFast_1_end: + MOVD R6, R1 + MOVD R9, R11 + LSR R1, R11, R11 + ADD R11<<1, R5, R15 + MOVHU (R15), R1 + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + MOVBU R1, R1 + ADD R1, R10, R10 + LSL R1, R9, R9 + MOVD R6, R1 + MOVD R9, R11 + LSR R1, R11, R11 + ADD R11<<1, R5, R15 + MOVHU (R15), R1 + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + MOVBU R1, R1 + ADD R1, R10, R10 + LSL R1, R9, R9 + REVW R0, R0 + CMP $0x20, R10 + BLT bitReader_fillFast_2_end + SUB $0x20, R10, R10 + SUB $0x04, R8, R8 + ADD R8, R7, R15 + MOVWU (R15), R11 + MOVD R10, R1 + LSL R1, R11, R11 + ORR R11, R9, R9 + +bitReader_fillFast_2_end: + MOVD R6, R1 + MOVD R9, R11 + LSR R1, R11, R11 + ADD R11<<1, R5, R15 + MOVHU (R15), R1 + UBFX $8, R1, $8, R16 + BFI $8, R16, $8, R0 + MOVBU R1, R1 + ADD R1, R10, R10 + LSL R1, R9, R9 + MOVD R6, R1 + MOVD R9, R11 + LSR R1, R11, R11 + ADD R11<<1, R5, R15 + MOVHU (R15), R1 + UBFX $8, R1, $8, R16 + BFI $0, R16, $8, R0 + MOVBU R1, R1 + ADD R1, R10, R10 + LSL R1, R9, R9 + REVW R0, R0 + + // Store the decoded values + MOVW R0, (R2) + ADD $0x04, R2, R2 + +loop_condition: + CMP $0x08, R8 + BGE main_loop + + // Update ctx structure + MOVD ctx+0(FP), R0 + MOVD 16(R0), R16 + SUB R16, R2, R2 + MOVD R2, 40(R0) + MOVD (R0), R0 + MOVD R8, 24(R0) + MOVD R9, 32(R0) + MOVB R10, 40(R0) + RET + + // Report error +error_max_decoded_size_exceeded: + MOVD ctx+0(FP), R0 + MOVD $-1, R1 + MOVD R1, 40(R0) + RET + +// skipped decompress1x_main_loop_bmi2 (generic twin preferred on arm64) diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_asm.go b/vendor/github.com/klauspost/compress/huff0/decompress_asm.go new file mode 100644 index 0000000000..4854dd4365 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/decompress_asm.go @@ -0,0 +1,193 @@ +//go:build (amd64 || arm64) && !appengine && !noasm && gc + +// This file contains the specialisation of Decoder.Decompress4X +// and Decoder.Decompress1X that use an asm implementation of their main loops. +// The asm function stubs and any per-arch dispatch live in decompress_amd64.go +// and decompress_arm64.go. +package huff0 + +import ( + "errors" + "fmt" +) + +// fallback8BitSize is the size where using Go version is faster. +const fallback8BitSize = 800 + +type decompress4xContext struct { + pbr *[4]bitReaderShifted + peekBits uint8 + out *byte + dstEvery int + tbl *dEntrySingle + decoded int + limit *byte +} + +// Decompress4X will decompress a 4X encoded stream. +// The length of the supplied input must match the end of a block exactly. +// The *capacity* of the dst slice must match the destination size of +// the uncompressed data exactly. +func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { + return nil, errors.New("no table loaded") + } + if len(src) < 6+(4*1) { + return nil, errors.New("input too small") + } + + use8BitTables := d.actualTableLog <= 8 + if cap(dst) < fallback8BitSize && use8BitTables { + return d.decompress4X8bit(dst, src) + } + + var br [4]bitReaderShifted + // Decode "jump table" + start := 6 + for i := range 3 { + length := int(src[i*2]) | (int(src[i*2+1]) << 8) + if start+length >= len(src) { + return nil, errors.New("truncated input (or invalid offset)") + } + err := br[i].init(src[start : start+length]) + if err != nil { + return nil, err + } + start += length + } + err := br[3].init(src[start:]) + if err != nil { + return nil, err + } + + // destination, offset to match first output + dstSize := cap(dst) + dst = dst[:dstSize] + out := dst + dstEvery := (dstSize + 3) / 4 + + const tlSize = 1 << tableLogMax + const tlMask = tlSize - 1 + single := d.dt.single[:tlSize] + + var decoded int + + if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) { + ctx := decompress4xContext{ + pbr: &br, + peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() + out: &out[0], + dstEvery: dstEvery, + tbl: &single[0], + limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last. + } + if use8BitTables { + decompress4x_8b_main_loop_asm(&ctx) + } else { + decompress4x_main_loop_asm(&ctx) + } + + decoded = ctx.decoded + out = out[decoded/4:] + } + + // Decode remaining. + remainBytes := dstEvery - (decoded / 4) + for i := range br { + offset := dstEvery * i + endsAt := min(offset+remainBytes, len(out)) + br := &br[i] + bitsLeft := br.remaining() + for bitsLeft > 0 { + br.fill() + if offset >= endsAt { + return nil, errors.New("corruption detected: stream overrun 4") + } + + // Read value and increment offset. + val := br.peekBitsFast(d.actualTableLog) + v := single[val&tlMask].entry + nBits := uint8(v) + br.advance(nBits) + bitsLeft -= uint(nBits) + out[offset] = uint8(v >> 8) + offset++ + } + if offset != endsAt { + return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) + } + decoded += offset - dstEvery*i + err = br.close() + if err != nil { + return nil, err + } + } + if dstSize != decoded { + return nil, errors.New("corruption detected: short output block") + } + return dst, nil +} + +type decompress1xContext struct { + pbr *bitReaderShifted + peekBits uint8 + out *byte + outCap int + tbl *dEntrySingle + decoded int +} + +// Error reported by asm implementations +const error_max_decoded_size_exeeded = -1 + +// Decompress1X will decompress a 1X encoded stream. +// The cap of the output buffer will be the maximum decompressed size. +// The length of the supplied input must match the end of a block exactly. +func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { + return nil, errors.New("no table loaded") + } + var br bitReaderShifted + err := br.init(src) + if err != nil { + return dst, err + } + maxDecodedSize := cap(dst) + dst = dst[:maxDecodedSize] + + const tlSize = 1 << tableLogMax + const tlMask = tlSize - 1 + + if maxDecodedSize >= 4 { + ctx := decompress1xContext{ + pbr: &br, + out: &dst[0], + outCap: maxDecodedSize, + peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() + tbl: &d.dt.single[0], + } + + decompress1x_main_loop_asm(&ctx) + if ctx.decoded == error_max_decoded_size_exeeded { + return nil, ErrMaxDecodedSizeExceeded + } + + dst = dst[:ctx.decoded] + } + + // br < 8, so uint8 is fine + bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead + for bitsLeft > 0 { + br.fill() + if len(dst) >= maxDecodedSize { + br.close() + return nil, ErrMaxDecodedSizeExceeded + } + v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] + nBits := uint8(v.entry) + br.advance(nBits) + bitsLeft -= nBits + dst = append(dst, uint8(v.entry>>8)) + } + return dst, br.close() +} diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_generic.go b/vendor/github.com/klauspost/compress/huff0/decompress_generic.go index 6103923222..e1bca5c686 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_generic.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm // This file contains a generic implementation of Decoder.Decompress4X. package huff0 diff --git a/vendor/github.com/klauspost/compress/internal/snapref/decode.go b/vendor/github.com/klauspost/compress/internal/snapref/decode.go index a2c82fcd22..584b7574b2 100644 --- a/vendor/github.com/klauspost/compress/internal/snapref/decode.go +++ b/vendor/github.com/klauspost/compress/internal/snapref/decode.go @@ -31,7 +31,7 @@ func DecodedLen(src []byte) (int, error) { // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { + if n <= 0 || n > 5 || v > 0xffffffff { return 0, 0, ErrCorrupt } diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index c11d7fa28e..a5aeeaed06 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -75,14 +75,47 @@ The above is fine for big encodes. However, whenever possible try to *reuse* the To reuse the encoder, you can use the `Reset(io.Writer)` function to change to another output. This will allow the encoder to reuse all resources and avoid wasteful allocations. -Currently stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part -of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change +By default, stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part +of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change in the future. So if you want to limit concurrency for future updates, specify the concurrency you would like. If you would like stream encoding to be done without spawning async goroutines, use `WithEncoderConcurrency(1)` which will compress input as each block is completed, blocking on writes until each has completed. +#### Parallel Stream Compression + +For maximum throughput on large streams, use `WithConcurrentBlocks(true)` together with +`WithEncoderConcurrency(n)` where n is the number of CPU cores you want to use. +This splits the input into large sections (jobs) that are compressed simultaneously by multiple goroutines, +similar to how the C zstd library does multithreaded compression. + +```Go +enc, err := zstd.NewWriter(out, + zstd.WithEncoderLevel(zstd.SpeedDefault), + zstd.WithEncoderConcurrency(runtime.GOMAXPROCS(0)), + zstd.WithConcurrentBlocks(true), +) +``` + +Each non-first job receives an overlap prefix from the previous job for match context, +so compression ratio is only marginally affected. Output is flushed in order, +producing a valid single-frame zstd stream. + +Benchmark on 1.8GB GOB stream (AMD Ryzen 9 9950X): + +| Level | 1 thread | 4 threads | 16 threads | 1T ratio | 16T ratio | +|---------|:----------:|:------------------:|:-------------------:|:--------:|:---------:| +| fastest | 783 MB/s | 2950 MB/s (3.8×) | 6939 MB/s (8.9×) | 12.24% | 12.26% | +| default | 728 MB/s | 2533 MB/s (3.5×) | 5340 MB/s (7.3×) | 10.67% | 10.68% | +| better | 434 MB/s | 1105 MB/s (2.5×) | 2206 MB/s (5.1×) | 9.14% | 9.21% | +| best | 129 MB/s | 367 MB/s (2.8×) | 884 MB/s (6.8×) | 8.48% | 8.63% | + +Notes: +* Not compatible with dictionary encoding. +* `Flush()` dispatches the current partial job, so latency-sensitive callers can force output. +* `EncodeAll` is unaffected — it uses its own concurrency via the encoder pool. + You can specify your desired compression level using `WithEncoderLevel()` option. Currently only pre-defined compression settings can be specified. diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go index 2329e996f8..51f9da0387 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -400,8 +400,9 @@ func (b *blockDec) decodeLiterals(in []byte, hist *history) (remain []byte, err } } var err error - // Use our out buffer. - huff.MaxDecodedSize = litRegenSize + // Decoder.Decompress* uses cap(dst) for the size limit. Do not write + // MaxDecodedSize on hist.huffTree: with a trained dictionary that + // pointer aliases the shared dict.litEnc and concurrent DecodeAll races. if fourStreams { literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) } else { @@ -673,10 +674,6 @@ func (b *blockDec) executeSequences(hist *history) error { hbytes := hist.b if len(hbytes) > hist.windowSize { hbytes = hbytes[len(hbytes)-hist.windowSize:] - // We do not need history anymore. - if hist.dict != nil { - hist.dict.content = nil - } } hist.decoders.windowSize = hist.windowSize hist.decoders.out = b.dst[:0] diff --git a/vendor/github.com/klauspost/compress/zstd/dict.go b/vendor/github.com/klauspost/compress/zstd/dict.go index 2ffbfdf379..67f40e7914 100644 --- a/vendor/github.com/klauspost/compress/zstd/dict.go +++ b/vendor/github.com/klauspost/compress/zstd/dict.go @@ -230,7 +230,7 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { } block := blockEnc{lowMem: false} block.init() - enc := encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) + var enc encoder if o.Level != 0 { eOpts := encoderOptions{ level: o.Level, @@ -242,6 +242,7 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { enc = eOpts.encoder() } else { o.Level = SpeedBestCompression + enc = encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) } var ( remain [256]int @@ -295,40 +296,81 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { if offset > 3 { newOffsets[offset-3]++ } else { - newOffsets[uint32(o.Offsets[offset-1])]++ + // Repeat codes reference the training Offsets. Skip unset + // (zero) entries so they are not ranked as real offsets. + prev := o.Offsets[offset-1] + if prev > 0 { + newOffsets[uint32(prev)]++ + } } } } // Find most used offsets. var sortedOffsets []uint32 for k := range newOffsets { + if k == 0 { + continue + } sortedOffsets = append(sortedOffsets, k) } sort.Slice(sortedOffsets, func(i, j int) bool { a, b := sortedOffsets[i], sortedOffsets[j] - if a == b { + ca, cb := newOffsets[a], newOffsets[b] + if ca == cb { // Prefer the longer offset - return sortedOffsets[i] > sortedOffsets[j] + return a > b } - return newOffsets[sortedOffsets[i]] > newOffsets[sortedOffsets[j]] + return ca > cb }) - if len(sortedOffsets) > 3 { - if debug { - print("Offsets:") - for i, v := range sortedOffsets { - if i > 20 { - break - } - printf("[%d: %d],", v, newOffsets[v]) + if debug { + print("Offsets:") + for i, v := range sortedOffsets { + if i > 20 { + break } - println("") + printf("[%d: %d],", v, newOffsets[v]) } - - sortedOffsets = sortedOffsets[:3] + println("") } - for i, v := range sortedOffsets { - o.Offsets[i] = int(v) + // Dictionary recent-offsets must be three positive values within the + // history. Ranked matches may be fewer (or empty when only unset + // repeat codes were seen), so fill remaining slots with defaults. + used := make(map[int]bool, 3) + var finalOffsets [3]int + nOff := 0 + for _, v := range sortedOffsets { + iv := int(v) + if iv <= 0 || iv > len(hist) || used[iv] { + continue + } + finalOffsets[nOff] = iv + used[iv] = true + nOff++ + if nOff == 3 { + break + } } + for _, def := range []int{1, 4, 8} { + if nOff == 3 { + break + } + if def <= len(hist) && !used[def] { + finalOffsets[nOff] = def + used[def] = true + nOff++ + } + } + for def := 1; nOff < 3 && def <= len(hist); def++ { + if !used[def] { + finalOffsets[nOff] = def + used[def] = true + nOff++ + } + } + if nOff < 3 { + return nil, fmt.Errorf("could not determine 3 valid dictionary offsets (history size %d)", len(hist)) + } + o.Offsets = finalOffsets if debug { println("New repeat offsets", o.Offsets) } @@ -336,6 +378,9 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { if nUsed == 0 || seqs == 0 { return nil, fmt.Errorf("%d blocks, %d sequences found", nUsed, seqs) } + if litTotal == 0 { + return nil, errors.New("0 literals found") + } if debug { println("Sequences:", seqs, "Blocks:", nUsed, "Literals:", litTotal) } @@ -516,11 +561,10 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[1]))) out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[2]))) out.Write(hist) + if _, err := loadDict(out.Bytes()); err != nil { + return nil, fmt.Errorf("built dictionary failed validation: %w", err) + } if debug { - _, err := loadDict(out.Bytes()) - if err != nil { - panic(err) - } i, err := InspectDictionary(out.Bytes()) if err != nil { panic(err) diff --git a/vendor/github.com/klauspost/compress/zstd/enc_base.go b/vendor/github.com/klauspost/compress/zstd/enc_base.go index c4de134a7a..c4fea575d6 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_base.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_base.go @@ -128,6 +128,34 @@ func (e *fastBase) matchlen(s, t int32, src []byte) int32 { return int32(matchLen(src[s:], src[t:])) } +// resetBasePrefix resets the encoder state and loads prefix as initial history. +// This is used for parallel job encoding where non-first jobs need overlap context. +// Rep offsets are set to defaults [1,4,8] (invalidated, matching C behavior). +func (e *fastBase) resetBasePrefix(prefix []byte) { + if e.blk == nil { + e.blk = &blockEnc{lowMem: e.lowMem} + e.blk.init() + } else { + e.blk.reset(nil) + } + e.blk.initNewEncode() + if e.crc == nil { + e.crc = xxhash.New() + } else { + e.crc.Reset() + } + e.blk.dictLitEnc = nil + e.ensureHist(len(prefix) + maxCompressedBlockSize) + // Bump cur so old table entries fall outside the window. + // When cur >= bufferReset, leave it; the first Encode call + // will shift/clear tables, preserving valid prefix entries. + if e.cur < e.bufferReset { + e.cur += e.maxMatchOff + int32(len(e.hist)) + } + e.hist = e.hist[:0] + e.hist = append(e.hist, prefix...) +} + // Reset the encoding table. func (e *fastBase) resetBase(d *dict, singleBlock bool) { if e.blk == nil { diff --git a/vendor/github.com/klauspost/compress/zstd/enc_best.go b/vendor/github.com/klauspost/compress/zstd/enc_best.go index 851799322b..c71382dde6 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_best.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_best.go @@ -551,3 +551,18 @@ func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { // Reset table to initial state copy(e.table[:], e.dictTable) } + +func (e *bestFastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur; i < end; i++ { + cv := load6432(prefix, i-e.cur) + h := hashLen(cv, bestLongTableBits, bestLongLen) + e.longTable[h] = prevEntry{offset: i, prev: e.longTable[h].offset} + h0 := hashLen(cv, bestShortTableBits, bestShortLen) + e.table[h0] = prevEntry{offset: i, prev: e.table[h0].offset} + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_better.go b/vendor/github.com/klauspost/compress/zstd/enc_better.go index 3305f09248..523d57f3ad 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_better.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -1096,6 +1096,20 @@ func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *betterFastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur; i < end; i += 2 { + cv := load6432(prefix, i-e.cur) + h := hashLen(cv, betterLongTableBits, betterLongLen) + e.longTable[h] = prevEntry{offset: i, prev: e.longTable[h].offset} + e.table[hashLen(cv>>8, betterShortTableBits, betterShortLen)] = tableEntry{val: uint32(cv >> 8), offset: i + 1} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) @@ -1229,6 +1243,10 @@ func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.allDirty = false } +func (e *betterFastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/betterLongTableShardSize] = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index 2fb6da112b..712ba7ab58 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -1037,6 +1037,18 @@ func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *doubleFastEncoder) ResetPrefix(prefix []byte) { + e.fastEncoder.ResetPrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur + 1; i < end; i += 2 { + cv := load6432(prefix, i-e.cur) + e.longTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{val: uint32(cv), offset: i} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { allDirty := e.allDirty @@ -1102,6 +1114,10 @@ func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { } } +func (e *doubleFastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *doubleFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/dLongTableShardSize] = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index 5e104f1a48..06045e2463 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -797,6 +797,19 @@ func (e *fastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *fastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + // Index every 4th + for i := e.cur + 1; i < end; i += 4 { + cv := load6432(prefix, i-e.cur) + e.table[hashLen(cv, tableBits, tableFastHashLen)] = tableEntry{val: uint32(cv), offset: i} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) @@ -866,6 +879,10 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.allDirty = false } +func (e *fastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *fastEncoderDict) markAllShardsDirty() { e.allDirty = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_jobs.go b/vendor/github.com/klauspost/compress/zstd/enc_jobs.go new file mode 100644 index 0000000000..95ce67ac05 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_jobs.go @@ -0,0 +1,352 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" + rdebug "runtime/debug" + "sync" +) + +type encJob struct { + prefix []byte // overlap from previous job (nil for first) + input []byte // job's own input data (swapped from filling) + last bool // last block of last job gets last=true + output []byte // compressed blocks (filled by worker) + err error // encoding error + done chan struct{} // closed when complete +} + +type jobState struct { + jobSize int + overlapSize int + filling []byte // accumulates input up to jobSize + nextPrefix []byte // overlap prefix prepared for the next dispatched job + + jobSeq int // next job sequence number + + jobCh chan *encJob // dispatch to workers + resultCh chan *encJob // ordered results to flusher + + workerWg sync.WaitGroup + flusherWg sync.WaitGroup + + mu sync.Mutex + flushedSeq int // last flushed sequence number + cond *sync.Cond + + flusherErr error + started bool + + inputPool sync.Pool // *[]byte buffers of jobSize cap + outputPool sync.Pool // *[]byte buffers for compressed output + overlapPool sync.Pool // *[]byte buffers for overlap prefixes +} + +func (e *Encoder) startJobWorkers() { + js := &e.state.jobs + n := e.o.concurrent + js.jobCh = make(chan *encJob, n) + js.resultCh = make(chan *encJob, n) + js.flushedSeq = 0 + js.cond = sync.NewCond(&js.mu) + + // Workers borrow encoders from the shared e.encoders pool per-job. + // Ensure the pool is initialized before any worker tries to borrow. + e.init.Do(e.initialize) + + for range n { + js.workerWg.Add(1) + go e.jobWorker() + } + js.flusherWg.Add(1) + go e.jobFlusher() + js.started = true +} + +func (e *Encoder) jobWorker() { + js := &e.state.jobs + defer js.workerWg.Done() + for job := range js.jobCh { + enc := <-e.encoders + e.compressJob(enc, job) + e.encoders <- enc + close(job.done) + } +} + +func (e *Encoder) compressJob(enc encoder, job *encJob) { + defer func() { + if r := recover(); r != nil { + job.err = fmt.Errorf("panic in parallel job: %v", r) + rdebug.PrintStack() + } + }() + + if len(job.prefix) > 0 { + enc.ResetPrefix(job.prefix) + } else { + enc.Reset(nil, false) + } + + data := job.input + if len(data) == 0 && job.last { + blk := enc.Block() + blk.reset(nil) + blk.last = true + blk.encodeRaw(nil) + job.output = append(job.output, blk.output...) + return + } + + blk := enc.Block() + for len(data) > 0 { + todo := data + if len(todo) > e.o.blockSize { + todo = todo[:e.o.blockSize] + } + data = data[len(todo):] + + blk.pushOffsets() + enc.Encode(blk, todo) + blk.last = len(data) == 0 && job.last + + err := blk.encode(todo, e.o.noEntropy, !e.o.allLitEntropy) + if err != nil { + job.err = err + return + } + job.output = append(job.output, blk.output...) + blk.reset(nil) + } +} + +func (js *jobState) getInputBuf(size int) []byte { + if v := js.inputPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:0] + } + } + return make([]byte, 0, size) +} + +func (js *jobState) putInputBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.inputPool.Put(&b) + } +} + +func (js *jobState) getOutputBuf(size int) []byte { + if v := js.outputPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:0] + } + } + return make([]byte, 0, size) +} + +func (js *jobState) putOutputBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.outputPool.Put(&b) + } +} + +func (js *jobState) getOverlapBuf(size int) []byte { + if v := js.overlapPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:size] + } + } + return make([]byte, size) +} + +func (js *jobState) putOverlapBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.overlapPool.Put(&b) + } +} + +func (e *Encoder) jobFlusher() { + js := &e.state.jobs + defer js.flusherWg.Done() + for job := range js.resultCh { + <-job.done + // Worker has fully exited compressJob, so the prefix is no longer + // in use. Return it to the pool regardless of outcome. + if job.prefix != nil { + js.putOverlapBuf(job.prefix) + job.prefix = nil + } + if job.err != nil { + js.mu.Lock() + js.flusherErr = job.err + js.cond.Broadcast() + js.mu.Unlock() + for range js.resultCh { + } + return + } + if len(job.output) > 0 { + _, err := e.state.w.Write(job.output) + if err != nil { + js.mu.Lock() + js.flusherErr = err + js.cond.Broadcast() + js.mu.Unlock() + for range js.resultCh { + } + return + } + e.state.nWritten += int64(len(job.output)) + } + // Return buffers to pools. + js.putInputBuf(job.input) + js.putOutputBuf(job.output) + job.input = nil + job.output = nil + + js.mu.Lock() + js.flushedSeq++ + js.cond.Broadcast() + js.mu.Unlock() + } +} + +func (e *Encoder) shutdownJobWorkers() { + js := &e.state.jobs + if !js.started { + return + } + close(js.jobCh) + js.workerWg.Wait() + close(js.resultCh) + js.flusherWg.Wait() + js.started = false +} + +// waitAllJobs blocks until all dispatched jobs have been flushed. +func (e *Encoder) waitAllJobs() { + js := &e.state.jobs + if !js.started { + return + } + js.mu.Lock() + for js.flushedSeq < js.jobSeq && js.flusherErr == nil { + js.cond.Wait() + } + js.mu.Unlock() +} + +func (e *Encoder) dispatchJob(final bool) error { + s := &e.state + js := &s.jobs + + js.mu.Lock() + fErr := js.flusherErr + js.mu.Unlock() + if fErr != nil { + return fErr + } + + if !s.headerWritten { + // Single-block optimization: fall through to encodeAll path. + if final && len(js.filling) > 0 && len(js.filling) <= e.o.blockSize { + s.current = e.encodeAll(s.encoder, js.filling, s.current[:0]) + var n2 int + n2, s.err = s.w.Write(s.current) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.nInput += int64(len(js.filling)) + s.current = s.current[:0] + js.filling = js.filling[:0] + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + if final && len(js.filling) == 0 && !e.o.fullZero { + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + + var tmp [maxHeaderSize]byte + fh := frameHeader{ + ContentSize: uint64(s.frameContentSize), + WindowSize: uint32(s.encoder.WindowSize(s.frameContentSize)), + SingleSegment: false, + Checksum: e.o.crc, + DictID: 0, + } + dst := fh.appendTo(tmp[:0]) + var n2 int + n2, s.err = s.w.Write(dst) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.headerWritten = true + } + + if len(js.filling) == 0 && !final { + return nil + } + + if !js.started { + e.startJobWorkers() + } + + // Estimate output size for pooled buffer. + outputEst := max(len(js.filling)/2, 512) + + job := &encJob{ + last: final, + done: make(chan struct{}), + output: js.getOutputBuf(outputEst), + } + + // Each job owns its prefix slice; the flusher returns it to the pool + // after <-job.done, so workers and dispatch never share a buffer. + if js.nextPrefix != nil { + job.prefix = js.nextPrefix + js.nextPrefix = nil + } + + // Build the next job's prefix from the tail of this job's input. + if !final && len(js.filling) > 0 { + overlapLen := min(js.overlapSize, len(js.filling)) + np := js.getOverlapBuf(overlapLen) + copy(np, js.filling[len(js.filling)-overlapLen:]) + js.nextPrefix = np + } + + // Swap filling buffer into job — zero-copy for the input data. + job.input = js.filling + js.filling = js.getInputBuf(js.jobSize) + + s.nInput += int64(len(job.input)) + js.jobSeq++ + + if final { + s.eofWritten = true + } + + js.resultCh <- job + js.jobCh <- job + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go index 0f2a00a003..6ee96d8730 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -38,6 +38,7 @@ type encoder interface { WindowSize(size int64) int32 UseBlock(*blockEnc) Reset(d *dict, singleBlock bool) + ResetPrefix(prefix []byte) } type encoderState struct { @@ -60,6 +61,9 @@ type encoderState struct { wg sync.WaitGroup // This waitgroup indicates we have a block encoding/writing. wWg sync.WaitGroup + + // Parallel job state (used when concurrentBlocks is enabled). + jobs jobState } // NewWriter will create a new Zstandard encoder. @@ -74,6 +78,9 @@ func NewWriter(w io.Writer, opts ...EOption) (*Encoder, error) { return nil, err } } + if e.o.concurrentBlocks && (e.o.dict != nil || e.o.concurrent <= 1) { + e.o.concurrentBlocks = false + } if w != nil { e.Reset(w) } @@ -95,12 +102,31 @@ func (e *Encoder) initialize() { // as a new, independent stream. func (e *Encoder) Reset(w io.Writer) { s := &e.state + + if e.o.concurrentBlocks { + e.shutdownJobWorkers() + js := &s.jobs + js.jobSize = e.o.jobSize() + js.overlapSize = e.o.overlapSize() + // js.filling is allocated lazily on first Write/ReadFrom so callers + // that only use EncodeAll don't pay the (up to ~32 MB) jobSize cost. + js.filling = js.filling[:0] + if js.nextPrefix != nil { + js.putOverlapBuf(js.nextPrefix) + js.nextPrefix = nil + } + js.jobSeq = 0 + js.flushedSeq = 0 + js.flusherErr = nil + js.started = false + } + s.wg.Wait() s.wWg.Wait() if cap(s.filling) == 0 { s.filling = make([]byte, 0, e.o.blockSize) } - if e.o.concurrent > 1 { + if e.o.concurrent > 1 && !e.o.concurrentBlocks { if cap(s.current) == 0 { s.current = make([]byte, 0, e.o.blockSize) } @@ -145,6 +171,9 @@ func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error { } } hasDict := e.o.dict != nil + if e.o.concurrentBlocks && hasDict { + e.o.concurrentBlocks = false + } if hadDict != hasDict { // Dict presence changed — encoder type must be recreated. e.state.encoder = nil @@ -176,6 +205,49 @@ func (e *Encoder) Write(p []byte) (n int, err error) { if s.eofWritten { return 0, ErrEncoderClosed } + if e.o.concurrentBlocks { + return e.writeJobs(p) + } + return e.writeBlocks(p) +} + +func (e *Encoder) writeJobs(p []byte) (n int, err error) { + s := &e.state + js := &s.jobs + jobSize := js.jobSize + if cap(js.filling) == 0 && len(p) > 0 { + js.filling = make([]byte, 0, jobSize) + } + for len(p) > 0 { + if len(p)+len(js.filling) < jobSize { + if e.o.crc { + _, _ = s.encoder.CRC().Write(p) + } + js.filling = append(js.filling, p...) + return n + len(p), nil + } + add := p + if len(p)+len(js.filling) > jobSize { + add = add[:jobSize-len(js.filling)] + } + if e.o.crc { + _, _ = s.encoder.CRC().Write(add) + } + js.filling = append(js.filling, add...) + p = p[len(add):] + n += len(add) + if len(js.filling) < jobSize { + return n, nil + } + if err := e.dispatchJob(false); err != nil { + return n, err + } + } + return n, nil +} + +func (e *Encoder) writeBlocks(p []byte) (n int, err error) { + s := &e.state for len(p) > 0 { if len(p)+len(s.filling) < e.o.blockSize { if e.o.crc { @@ -374,6 +446,10 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { println("Using ReadFrom") } + if e.o.concurrentBlocks { + return e.readFromJobs(r) + } + // Flush any current writes. if len(e.state.filling) > 0 { if err := e.nextBlock(false); err != nil { @@ -387,7 +463,6 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { if e.o.crc { _, _ = e.state.encoder.CRC().Write(src[:n2]) } - // src is now the unfilled part... src = src[n2:] n += int64(n2) switch err { @@ -420,15 +495,63 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { } } +func (e *Encoder) readFromJobs(r io.Reader) (n int64, err error) { + js := &e.state.jobs + jobSize := js.jobSize + + // Flush any current filling. + if len(js.filling) > 0 { + if err := e.dispatchJob(false); err != nil { + return 0, err + } + } + + if cap(js.filling) < jobSize { + js.filling = make([]byte, 0, jobSize) + } + js.filling = js.filling[:jobSize] + src := js.filling + for { + n2, err := r.Read(src) + if e.o.crc { + _, _ = e.state.encoder.CRC().Write(src[:n2]) + } + src = src[n2:] + n += int64(n2) + switch err { + case io.EOF: + js.filling = js.filling[:len(js.filling)-len(src)] + return n, nil + case nil: + default: + e.state.err = err + return n, err + } + if len(src) > 0 { + continue + } + if err = e.dispatchJob(false); err != nil { + return n, err + } + if cap(js.filling) < jobSize { + js.filling = make([]byte, 0, jobSize) + } + js.filling = js.filling[:jobSize] + src = js.filling + } +} + // Flush will send the currently written data to output // and block until everything has been written. // This should only be used on rare occasions where pushing the currently queued data is critical. func (e *Encoder) Flush() error { s := &e.state + if e.o.concurrentBlocks { + return e.flushJobs() + } if len(s.filling) > 0 { err := e.nextBlock(false) if err != nil { - // Ignore Flush after Close. if errors.Is(s.err, ErrEncoderClosed) { return nil } @@ -438,7 +561,6 @@ func (e *Encoder) Flush() error { s.wg.Wait() s.wWg.Wait() if s.err != nil { - // Ignore Flush after Close. if errors.Is(s.err, ErrEncoderClosed) { return nil } @@ -447,6 +569,20 @@ func (e *Encoder) Flush() error { return s.writeErr } +func (e *Encoder) flushJobs() error { + js := &e.state.jobs + if len(js.filling) > 0 { + if err := e.dispatchJob(false); err != nil { + return err + } + } + e.waitAllJobs() + js.mu.Lock() + fErr := js.flusherErr + js.mu.Unlock() + return fErr +} + // Close will flush the final output and close the stream. // The function will block until everything has been written. // The Encoder can still be re-used after calling this. @@ -455,12 +591,16 @@ func (e *Encoder) Close() error { if s.encoder == nil { return nil } + if e.o.concurrentBlocks { + return e.closeJobs() + } if s.w == nil { if len(s.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { return nil } return errors.New("zstd: encoder has no writer") } + err := e.nextBlock(true) if err != nil { if errors.Is(s.err, ErrEncoderClosed) { @@ -511,6 +651,68 @@ func (e *Encoder) Close() error { return s.err } +func (e *Encoder) closeJobs() error { + s := &e.state + js := &s.jobs + + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + + if s.w == nil { + if len(js.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { + return nil + } + return errors.New("zstd: encoder has no writer") + } + + if err := e.dispatchJob(true); err != nil { + e.shutdownJobWorkers() + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + return err + } + + if s.frameContentSize > 0 && s.nInput != s.frameContentSize { + e.shutdownJobWorkers() + return fmt.Errorf("frame content size %d given, but %d bytes was written", s.frameContentSize, s.nInput) + } + + if s.fullFrameWritten { + e.shutdownJobWorkers() + s.err = ErrEncoderClosed + return nil + } + + e.shutdownJobWorkers() + if js.flusherErr != nil { + return js.flusherErr + } + + // Write CRC + if e.o.crc { + var tmp [4]byte + _, s.err = s.w.Write(s.encoder.AppendCRC(tmp[:0])) + s.nWritten += 4 + } + + // Add padding + if s.err == nil && e.o.pad > 0 { + add := calcSkippableFrame(s.nWritten, int64(e.o.pad)) + frame, err := skippableFrame(js.filling[:0], add, rand.Reader) + if err != nil { + return err + } + _, s.err = s.w.Write(frame) + } + if s.err == nil { + s.err = ErrEncoderClosed + return nil + } + return s.err +} + // EncodeAll will encode all input in src and append it to dst. // This function can be called concurrently, but each call will only run on a single goroutine. // If empty input is given, nothing is returned, unless WithZeroFrames is specified. diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index e217be0a17..a808149673 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -14,22 +14,23 @@ type EOption func(*encoderOptions) error // options retains accumulated state of multiple options. type encoderOptions struct { - resetOpt bool - concurrent int - level EncoderLevel - single *bool - pad int - blockSize int - windowSize int - crc bool - fullZero bool - noEntropy bool - allLitEntropy bool - customWindow bool - customALEntropy bool - customBlockSize bool - lowMem bool - dict *dict + resetOpt bool + concurrent int + level EncoderLevel + single *bool + pad int + blockSize int + windowSize int + crc bool + fullZero bool + noEntropy bool + allLitEntropy bool + customWindow bool + customALEntropy bool + customBlockSize bool + lowMem bool + dict *dict + concurrentBlocks bool } func (o *encoderOptions) setDefault() { @@ -333,6 +334,42 @@ func WithLowerEncoderMem(b bool) EOption { } } +// WithConcurrentBlocks enables job-based parallel compression for streams. +// When enabled and concurrent > 1, input is split into large sections (jobs) +// that are compressed simultaneously by multiple goroutines. +// Each non-first job receives an overlap prefix from the previous job for match context. +// Output is flushed in order, producing a valid single-frame zstd stream. +// +// Currently disabled when used with dictionary encoding. +// Cannot be changed with ResetWithOptions. +func WithConcurrentBlocks(b bool) EOption { + return func(o *encoderOptions) error { + if o.resetOpt && b != o.concurrentBlocks { + return errors.New("WithConcurrentBlocks cannot be changed on Reset") + } + o.concurrentBlocks = b + return nil + } +} + +// jobSize returns the input section size per parallel job. +func (o *encoderOptions) jobSize() int { + s := max(o.windowSize*4, 512<<10) + return s +} + +// overlapSize returns the overlap prefix size for parallel jobs. +func (o *encoderOptions) overlapSize() int { + switch o.level { + case SpeedBestCompression: + return o.windowSize / 2 + case SpeedBetterCompression: + return o.windowSize / 4 + default: + return o.windowSize / 8 + } +} + // WithEncoderDict allows to register a dictionary that will be used for the encode. // // The slice dict must be in the [dictionary format] produced by diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s index bcde398695..deeadc49eb 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s @@ -1,4 +1,4 @@ -// Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. +// Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s new file mode 100644 index 0000000000..77ee3913f0 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s @@ -0,0 +1,153 @@ +// Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc && !noasm + +// func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int +TEXT ·buildDtable_asm(SB), $0-24 + MOVD ctx+8(FP), R1 + MOVD s+0(FP), R6 + + // Load values + MOVBU 4098(R6), R2 + MOVD $0, R0 + MOVD $1, R16 + LSL R2, R16, R16 + ORR R16, R0, R0 + MOVD (R1), R3 + MOVD 16(R1), R5 + SUB $1, R0, R7 + MOVD 8(R1), R1 + MOVHU 4096(R6), R6 + + // End load values + // Init, lay down lowprob symbols + MOVD $0, R8 + JMP init_main_loop_condition + +init_main_loop: + ADD R8<<1, R1, R15 + MOVH (R15), R9 + AND $0xffff, R9, R15 + MOVD $-1, R16 + AND $0xffff, R16, R16 + CMP R16, R15 + BNE do_not_update_high_threshold + ADD R7<<3, R5, R15 + MOVB R8, 1(R15) + SUB $1, R7, R7 + MOVD $0x0000000000000001, R9 + +do_not_update_high_threshold: + ADD R8<<1, R3, R15 + MOVH R9, (R15) + ADD $1, R8, R8 + +init_main_loop_condition: + CMP R6, R8 + BLT init_main_loop + + // Spread symbols + // Calculate table step + MOVD R0, R8 + LSR $0x01, R8, R8 + MOVD R0, R9 + LSR $0x03, R9, R9 + ADD R9, R8, R8 + ADD $3, R8, R8 + + // Fill add bits values + SUB $1, R0, R9 + MOVD $0, R10 + MOVD $0, R11 + JMP spread_main_loop_condition + +spread_main_loop: + MOVD $0, R12 + ADD R11<<1, R1, R15 + MOVH (R15), R13 + JMP spread_inner_loop_condition + +spread_inner_loop: + ADD R10<<3, R5, R15 + MOVB R11, 1(R15) + +adjust_position: + ADD R8, R10, R10 + AND R9, R10, R10 + CMP R7, R10 + BGT adjust_position + ADD $1, R12, R12 + +spread_inner_loop_condition: + CMP R13, R12 + BLT spread_inner_loop + ADD $1, R11, R11 + +spread_main_loop_condition: + CMP R6, R11 + BLT spread_main_loop + TST R10, R10 + BEQ spread_check_ok + MOVD ctx+8(FP), R0 + MOVD R10, 24(R0) + MOVD $+1, R16 + MOVD R16, ret+16(FP) + RET + +spread_check_ok: + // Build Decoding table + MOVD $0, R6 + +build_table_main_table: + ADD R6<<3, R5, R15 + MOVBU 1(R15), R1 + ADD R1<<1, R3, R15 + MOVHU (R15), R7 + ADD $1, R7, R8 + ADD R1<<1, R3, R15 + MOVH R8, (R15) + MOVD R7, R8 + CLZ R8, R16 + MOVD $63, R8 + SUB R16, R8, R8 + MOVD R2, R1 + SUB R8, R1, R1 + LSL R1, R7, R7 + SUB R0, R7, R7 + ADD R6<<3, R5, R15 + MOVB R1, (R15) + ADD R6<<3, R5, R15 + MOVH R7, 2(R15) + CMP R0, R7 + BLE build_table_check1_ok + MOVD ctx+8(FP), R1 + MOVD R7, 24(R1) + MOVD R0, 32(R1) + MOVD $+2, R16 + MOVD R16, ret+16(FP) + RET + +build_table_check1_ok: + AND $0xff, R1, R15 + AND $0xff, R1, R16 + TST R16, R15 + BNE build_table_check2_ok + AND $0xffff, R7, R15 + AND $0xffff, R6, R16 + CMP R16, R15 + BNE build_table_check2_ok + MOVD ctx+8(FP), R0 + MOVD R7, 24(R0) + MOVD R6, 32(R0) + MOVD $+3, R16 + MOVD R16, ret+16(FP) + RET + +build_table_check2_ok: + ADD $1, R6, R6 + CMP R0, R6 + BLT build_table_main_table + MOVD $+0, R16 + MOVD R16, ret+16(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go similarity index 81% rename from vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go rename to vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go index b8c8607b5d..4ffc7e3c9f 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go @@ -1,4 +1,4 @@ -//go:build amd64 && !appengine && !noasm && gc +//go:build (amd64 || arm64) && !appengine && !noasm && gc package zstd @@ -6,6 +6,10 @@ import ( "fmt" ) +// buildDtable_asm is generated by _generate/gen_fse.go and lowered to each +// architecture (amd64 by goasm, arm64 by the avo arm64 lowering printer). The +// Go side is identical across architectures, so it lives here. + type buildDtableAsmContext struct { // inputs stateTable *uint16 @@ -18,7 +22,7 @@ type buildDtableAsmContext struct { errParam2 uint64 } -// buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable. +// buildDtable_asm is an assembly implementation of fseDecoder.buildDtable. // Function returns non-zero exit code on error. // //go:noescape diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go index 2138f8091a..38fd2ccb2a 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go index 18c3703ddc..1281da885c 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -3,30 +3,47 @@ package zstd import ( - "fmt" - "io" - "github.com/klauspost/compress/internal/cpuinfo" ) -type decodeSyncAsmContext struct { - llTable []decSymbol - mlTable []decSymbol - ofTable []decSymbol - llState uint64 - mlState uint64 - ofState uint64 - iteration int - litRemain int - out []byte - outPosition int - literals []byte - litPosition int - history []byte - windowSize int - ll int // set on error (not for all errors, please refer to _generate/gen.go) - ml int // set on error (not for all errors, please refer to _generate/gen.go) - mo int // set on error (not for all errors, please refer to _generate/gen.go) +// The shared decode/decodeSync/executeSimple wrappers and context structs live +// in seqdec_asm.go; this file only declares the amd64 asm routines and the +// dispatch helpers that pick the BMI2 / non-BMI2 (and 56-bit / safe) variant. + +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode_56_amd64 implements the main loop of sequenceDecs in x86 asm. +// +//go:noescape +func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode_bmi2 implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// +//go:noescape +func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode_56_bmi2 implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// +//go:noescape +func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// decodeAsm runs the sequenceDecs decode loop, choosing the BMI2 / 56-bit variant. +func decodeAsm(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext, lte56bits bool) int { + if cpuinfo.HasBMI2() { + if lte56bits { + return sequenceDecs_decode_56_bmi2(s, br, ctx) + } + return sequenceDecs_decode_bmi2(s, br, ctx) + } + if lte56bits { + return sequenceDecs_decode_56_amd64(s, br, ctx) + } + return sequenceDecs_decode_amd64(s, br, ctx) } // sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. @@ -51,273 +68,18 @@ func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *dec //go:noescape func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// decode sequences from the stream with the provided history but without a dictionary. -func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { - if len(s.dict) > 0 { - return false, nil - } - if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { - return false, nil - } - - // FIXME: Using unsafe memory copies leads to rare, random crashes - // with fuzz testing. It is therefore disabled for now. - const useSafe = true - /* - useSafe := false - if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc { - useSafe = true - } - if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) { - useSafe = true - } - if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { - useSafe = true - } - */ - - br := s.br - - maxBlockSize := min(s.windowSize, maxCompressedBlockSize) - - ctx := decodeSyncAsmContext{ - llTable: s.litLengths.fse.dt[:maxTablesize], - mlTable: s.matchLengths.fse.dt[:maxTablesize], - ofTable: s.offsets.fse.dt[:maxTablesize], - llState: uint64(s.litLengths.state.state), - mlState: uint64(s.matchLengths.state.state), - ofState: uint64(s.offsets.state.state), - iteration: s.nSeqs - 1, - litRemain: len(s.literals), - out: s.out, - outPosition: len(s.out), - literals: s.literals, - windowSize: s.windowSize, - history: hist, - } - - s.seqSize = 0 - startSize := len(s.out) - - var errCode int +// decodeSyncAsm runs the decodeSync loop, choosing the BMI2 / safe variant. +func decodeSyncAsm(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext, safe bool) int { if cpuinfo.HasBMI2() { - if useSafe { - errCode = sequenceDecs_decodeSync_safe_bmi2(s, br, &ctx) - } else { - errCode = sequenceDecs_decodeSync_bmi2(s, br, &ctx) - } - } else { - if useSafe { - errCode = sequenceDecs_decodeSync_safe_amd64(s, br, &ctx) - } else { - errCode = sequenceDecs_decodeSync_amd64(s, br, &ctx) + if safe { + return sequenceDecs_decodeSync_safe_bmi2(s, br, ctx) } + return sequenceDecs_decodeSync_bmi2(s, br, ctx) } - switch errCode { - case noError: - break - - case errorMatchLenOfsMismatch: - return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) - - case errorMatchLenTooBig: - return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) - - case errorMatchOffTooBig: - return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", - ctx.mo, ctx.outPosition+len(hist)-startSize) - - case errorNotEnoughLiterals: - return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", - ctx.ll, ctx.litRemain+ctx.ll) - - case errorOverread: - return true, io.ErrUnexpectedEOF - - case errorNotEnoughSpace: - size := ctx.outPosition + ctx.ll + ctx.ml - if debugDecoder { - println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) - } - return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - - default: - return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) + if safe { + return sequenceDecs_decodeSync_safe_amd64(s, br, ctx) } - - s.seqSize += ctx.litRemain - if s.seqSize > maxBlockSize { - return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - } - err := br.close() - if err != nil { - printf("Closing sequences: %v, %+v\n", err, *br) - return true, err - } - - s.literals = s.literals[ctx.litPosition:] - t := ctx.outPosition - s.out = s.out[:t] - - // Add final literals - s.out = append(s.out, s.literals...) - if debugDecoder { - t += len(s.literals) - if t != len(s.out) { - panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) - } - } - - return true, nil -} - -// -------------------------------------------------------------------------------- - -type decodeAsmContext struct { - llTable []decSymbol - mlTable []decSymbol - ofTable []decSymbol - llState uint64 - mlState uint64 - ofState uint64 - iteration int - seqs []seqVals - litRemain int -} - -const noError = 0 - -// error reported when mo == 0 && ml > 0 -const errorMatchLenOfsMismatch = 1 - -// error reported when ml > maxMatchLen -const errorMatchLenTooBig = 2 - -// error reported when mo > available history or mo > s.windowSize -const errorMatchOffTooBig = 3 - -// error reported when the sum of literal lengths exeeceds the literal buffer size -const errorNotEnoughLiterals = 4 - -// error reported when capacity of `out` is too small -const errorNotEnoughSpace = 5 - -// error reported when bits are overread. -const errorOverread = 6 - -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. -// -// Please refer to seqdec_generic.go for the reference implementation. -// -//go:noescape -func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. -// -// Please refer to seqdec_generic.go for the reference implementation. -// -//go:noescape -func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. -// -//go:noescape -func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. -// -//go:noescape -func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - -// decode sequences from the stream without the provided history. -func (s *sequenceDecs) decode(seqs []seqVals) error { - br := s.br - - maxBlockSize := min(s.windowSize, maxCompressedBlockSize) - - ctx := decodeAsmContext{ - llTable: s.litLengths.fse.dt[:maxTablesize], - mlTable: s.matchLengths.fse.dt[:maxTablesize], - ofTable: s.offsets.fse.dt[:maxTablesize], - llState: uint64(s.litLengths.state.state), - mlState: uint64(s.matchLengths.state.state), - ofState: uint64(s.offsets.state.state), - seqs: seqs, - iteration: len(seqs) - 1, - litRemain: len(s.literals), - } - - if debugDecoder { - println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") - } - - s.seqSize = 0 - lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 - var errCode int - if cpuinfo.HasBMI2() { - if lte56bits { - errCode = sequenceDecs_decode_56_bmi2(s, br, &ctx) - } else { - errCode = sequenceDecs_decode_bmi2(s, br, &ctx) - } - } else { - if lte56bits { - errCode = sequenceDecs_decode_56_amd64(s, br, &ctx) - } else { - errCode = sequenceDecs_decode_amd64(s, br, &ctx) - } - } - if errCode != 0 { - i := len(seqs) - ctx.iteration - 1 - switch errCode { - case errorMatchLenOfsMismatch: - ml := ctx.seqs[i].ml - return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) - - case errorMatchLenTooBig: - ml := ctx.seqs[i].ml - return fmt.Errorf("match len (%d) bigger than max allowed length", ml) - - case errorNotEnoughLiterals: - ll := ctx.seqs[i].ll - return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) - case errorOverread: - return io.ErrUnexpectedEOF - } - - return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) - } - - if ctx.litRemain < 0 { - return fmt.Errorf("literal count is too big: total available %d, total requested %d", - len(s.literals), len(s.literals)-ctx.litRemain) - } - - s.seqSize += ctx.litRemain - if s.seqSize > maxBlockSize { - return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - } - if debugDecoder { - println("decode: ", br.remain(), "bits remain on stream. code:", errCode) - } - err := br.close() - if err != nil { - printf("Closing sequences: %v, %+v\n", err, *br) - } - return err -} - -// -------------------------------------------------------------------------------- - -type executeAsmContext struct { - seqs []seqVals - seqIndex int - out []byte - history []byte - literals []byte - outPosition int - litPosition int - windowSize int + return sequenceDecs_decodeSync_amd64(s, br, ctx) } // sequenceDecs_executeSimple_amd64 implements the main loop of sequenceDecs.executeSimple in x86 asm. @@ -334,54 +96,10 @@ func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool //go:noescape func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool -// executeSimple handles cases when dictionary is not used. -func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { - // Ensure we have enough output size... - if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { - addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc - s.out = append(s.out, make([]byte, addBytes)...) - s.out = s.out[:len(s.out)-addBytes] +// executeSimpleAsm runs the executeSimple loop, choosing the safe variant. +func executeSimpleAsm(ctx *executeAsmContext, safe bool) bool { + if safe { + return sequenceDecs_executeSimple_safe_amd64(ctx) } - - if debugDecoder { - printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) - } - - var t = len(s.out) - out := s.out[:t+s.seqSize] - - ctx := executeAsmContext{ - seqs: seqs, - seqIndex: 0, - out: out, - history: hist, - outPosition: t, - litPosition: 0, - literals: s.literals, - windowSize: s.windowSize, - } - var ok bool - if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { - ok = sequenceDecs_executeSimple_safe_amd64(&ctx) - } else { - ok = sequenceDecs_executeSimple_amd64(&ctx) - } - if !ok { - return fmt.Errorf("match offset (%d) bigger than current history (%d)", - seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) - } - s.literals = s.literals[ctx.litPosition:] - t = ctx.outPosition - - // Add final literals - copy(out[t:], s.literals) - if debugDecoder { - t += len(s.literals) - if t != len(out) { - panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) - } - } - s.out = out - - return nil + return sequenceDecs_executeSimple_amd64(ctx) } diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s index a708ca6d3d..4d3188ff49 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -1,4 +1,4 @@ -// Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT. +// Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm @@ -2081,7 +2081,7 @@ sequenceDecs_decodeSync_amd64_match_len_ofs_ok: MOVQ 16(SP), R13 // Check if we have enough space in s.out - LEAQ (AX)(R13*1), R14 + LEAQ 16(AX)(R13*1), R14 ADDQ R10, R14 CMPQ R14, 32(SP) JA error_not_enough_space @@ -2601,7 +2601,7 @@ sequenceDecs_decodeSync_bmi2_match_len_ofs_ok: MOVQ 16(SP), R13 // Check if we have enough space in s.out - LEAQ (CX)(R13*1), R14 + LEAQ 16(CX)(R13*1), R14 ADDQ R9, R14 CMPQ R14, 32(SP) JA error_not_enough_space diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go new file mode 100644 index 0000000000..5ad262acff --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go @@ -0,0 +1,70 @@ +//go:build arm64 && !appengine && !noasm && gc + +package zstd + +// The shared decode/decodeSync/executeSimple wrappers and context structs live +// in seqdec_asm.go; this file only declares the arm64 asm routines (generated +// by the avo arm64 lowering printer) and the dispatch helpers. arm64 has no +// BMI2, so each helper selects only between the 56-bit / safe variants. + +// sequenceDecs_decode_arm64 implements the main loop of sequenceDecs in arm64 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decode_arm64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode_56_arm64 implements the main loop of sequenceDecs in arm64 asm. +// +//go:noescape +func sequenceDecs_decode_56_arm64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// decodeAsm runs the sequenceDecs decode loop, choosing the 56-bit variant. +func decodeAsm(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext, lte56bits bool) int { + if lte56bits { + return sequenceDecs_decode_56_arm64(s, br, ctx) + } + return sequenceDecs_decode_arm64(s, br, ctx) +} + +// sequenceDecs_decodeSync_arm64 implements the main loop of sequenceDecs.decodeSync in arm64 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decodeSync_arm64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// sequenceDecs_decodeSync_safe_arm64 does the same as above, but does not write more than output buffer. +// +//go:noescape +func sequenceDecs_decodeSync_safe_arm64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// decodeSyncAsm runs the decodeSync loop, choosing the safe variant. +func decodeSyncAsm(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext, safe bool) int { + if safe { + return sequenceDecs_decodeSync_safe_arm64(s, br, ctx) + } + return sequenceDecs_decodeSync_arm64(s, br, ctx) +} + +// sequenceDecs_executeSimple_arm64 implements the main loop of sequenceDecs.executeSimple in arm64 asm. +// +// Returns false if a match offset is too big. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_executeSimple_arm64(ctx *executeAsmContext) bool + +// Same as above, but with safe memcopies +// +//go:noescape +func sequenceDecs_executeSimple_safe_arm64(ctx *executeAsmContext) bool + +// executeSimpleAsm runs the executeSimple loop, choosing the safe variant. +func executeSimpleAsm(ctx *executeAsmContext, safe bool) bool { + if safe { + return sequenceDecs_executeSimple_safe_arm64(ctx) + } + return sequenceDecs_executeSimple_arm64(ctx) +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s new file mode 100644 index 0000000000..6f54ef21ba --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s @@ -0,0 +1,2750 @@ +// Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc && !noasm + +// func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_arm64(SB), $8-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, 8(RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD 104(R0), R9 + MOVD s+0(FP), R0 + MOVD 144(R0), R10 + MOVD 152(R0), R11 + MOVD 160(R0), R12 + +sequenceDecs_decode_amd64_main_loop: + MOVD 8(RSP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decode_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_amd64_fill_end + +sequenceDecs_decode_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_amd64_fill_byte_by_byte + +sequenceDecs_decode_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_of_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_of_update_zero: + MOVD R0, 16(R9) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_ml_update_zero: + MOVD R0, 8(R9) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decode_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_amd64_fill_2_end + +sequenceDecs_decode_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_amd64_fill_2_byte_by_byte + +sequenceDecs_decode_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_ll_update_zero: + MOVD R0, (R9) + + // Fill bitreader for state updates + MOVD R13, 8(RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R13 + LSRW $0x10, R6, R6 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + BFI $0, R13, $8, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R13 + LSRW $0x10, R7, R7 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + BFI $0, R13, $8, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R13 + LSRW $0x10, R8, R8 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + BFI $0, R13, $8, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decode_amd64_skip_update: + // Adjust offset + MOVD 16(R9), R1 + CMP $0x01, R0 + BLS sequenceDecs_decode_amd64_adjust_offsetB_1_or_0 + MOVD R11, R12 + MOVD R10, R11 + MOVD R1, R10 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offsetB_1_or_0: + MOVD (R9), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decode_amd64_adjust_offset_maybezero + ADD $1, R1, R1 + JMP sequenceDecs_decode_amd64_adjust_offset_nonzero + +sequenceDecs_decode_amd64_adjust_offset_maybezero: + TST R1, R1 + BNE sequenceDecs_decode_amd64_adjust_offset_nonzero + MOVD R10, R1 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offset_nonzero: + CMP $0x01, R1 + BLO sequenceDecs_decode_amd64_adjust_zero + BEQ sequenceDecs_decode_amd64_adjust_one + CMP $0x02, R1 + BHI sequenceDecs_decode_amd64_adjust_three + JMP sequenceDecs_decode_amd64_adjust_two + +sequenceDecs_decode_amd64_adjust_zero: + MOVD R10, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_one: + MOVD R11, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_two: + MOVD R12, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_three: + SUB $1, R10, R0 + +sequenceDecs_decode_amd64_adjust_test_temp_valid: + TST R0, R0 + BNE sequenceDecs_decode_amd64_adjust_temp_valid + MOVD $0x00000001, R0 + +sequenceDecs_decode_amd64_adjust_temp_valid: + CMP $0x01, R1 + CSEL NE, R11, R12, R12 + MOVD R10, R11 + MOVD R0, R10 + MOVD R0, R1 + +sequenceDecs_decode_amd64_after_adjust: + MOVD R1, 16(R9) + + // Check values + MOVD 8(R9), R0 + MOVD (R9), R13 + ADD R13, R0, R14 + MOVD s+0(FP), R4 + MOVD 256(R4), R16 + ADD R14, R16, R16 + MOVD R16, 256(R4) + MOVD ctx+16(FP), R14 + MOVD 128(R14), R16 + SUBS R13, R16, R16 + MOVD R16, 128(R14) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decode_amd64_error_match_len_too_big + TST R1, R1 + BNE sequenceDecs_decode_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decode_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_amd64_match_len_ofs_ok: + ADD $0x18, R9, R9 + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decode_amd64_main_loop + MOVD s+0(FP), R0 + MOVD R10, 144(R0) + MOVD R11, 152(R0) + MOVD R12, 160(R0) + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_amd64_error_match_len_ofs_mismatch: + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_amd64_error_match_len_too_big: + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + +// func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_56_arm64(SB), $8-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, 8(RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD 104(R0), R9 + MOVD s+0(FP), R0 + MOVD 144(R0), R10 + MOVD 152(R0), R11 + MOVD 160(R0), R12 + +sequenceDecs_decode_56_amd64_main_loop: + MOVD 8(RSP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decode_56_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_56_amd64_fill_end + +sequenceDecs_decode_56_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_56_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_56_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_56_amd64_fill_byte_by_byte + +sequenceDecs_decode_56_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_56_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_of_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_of_update_zero: + MOVD R0, 16(R9) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_ml_update_zero: + MOVD R0, 8(R9) + + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_ll_update_zero: + MOVD R0, (R9) + + // Fill bitreader for state updates + MOVD R13, 8(RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_56_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R13 + LSRW $0x10, R6, R6 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + BFI $0, R13, $8, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R13 + LSRW $0x10, R7, R7 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + BFI $0, R13, $8, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R13 + LSRW $0x10, R8, R8 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + BFI $0, R13, $8, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decode_56_amd64_skip_update: + // Adjust offset + MOVD 16(R9), R1 + CMP $0x01, R0 + BLS sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0 + MOVD R11, R12 + MOVD R10, R11 + MOVD R1, R10 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0: + MOVD (R9), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decode_56_amd64_adjust_offset_maybezero + ADD $1, R1, R1 + JMP sequenceDecs_decode_56_amd64_adjust_offset_nonzero + +sequenceDecs_decode_56_amd64_adjust_offset_maybezero: + TST R1, R1 + BNE sequenceDecs_decode_56_amd64_adjust_offset_nonzero + MOVD R10, R1 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offset_nonzero: + CMP $0x01, R1 + BLO sequenceDecs_decode_56_amd64_adjust_zero + BEQ sequenceDecs_decode_56_amd64_adjust_one + CMP $0x02, R1 + BHI sequenceDecs_decode_56_amd64_adjust_three + JMP sequenceDecs_decode_56_amd64_adjust_two + +sequenceDecs_decode_56_amd64_adjust_zero: + MOVD R10, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_one: + MOVD R11, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_two: + MOVD R12, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_three: + SUB $1, R10, R0 + +sequenceDecs_decode_56_amd64_adjust_test_temp_valid: + TST R0, R0 + BNE sequenceDecs_decode_56_amd64_adjust_temp_valid + MOVD $0x00000001, R0 + +sequenceDecs_decode_56_amd64_adjust_temp_valid: + CMP $0x01, R1 + CSEL NE, R11, R12, R12 + MOVD R10, R11 + MOVD R0, R10 + MOVD R0, R1 + +sequenceDecs_decode_56_amd64_after_adjust: + MOVD R1, 16(R9) + + // Check values + MOVD 8(R9), R0 + MOVD (R9), R13 + ADD R13, R0, R14 + MOVD s+0(FP), R4 + MOVD 256(R4), R16 + ADD R14, R16, R16 + MOVD R16, 256(R4) + MOVD ctx+16(FP), R14 + MOVD 128(R14), R16 + SUBS R13, R16, R16 + MOVD R16, 128(R14) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decode_56_amd64_error_match_len_too_big + TST R1, R1 + BNE sequenceDecs_decode_56_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_56_amd64_match_len_ofs_ok: + ADD $0x18, R9, R9 + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decode_56_amd64_main_loop + MOVD s+0(FP), R0 + MOVD R10, 144(R0) + MOVD R11, 152(R0) + MOVD R12, 160(R0) + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch: + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_56_amd64_error_match_len_too_big: + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decode_bmi2 (generic twin preferred on arm64) + +// skipped sequenceDecs_decode_56_bmi2 (generic twin preferred on arm64) + +// func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_arm64(SB), $8-9 + MOVD ctx+0(FP), R9 + MOVD 8(R9), R1 + TST R1, R1 + BEQ empty_seqs + MOVD (R9), R0 + MOVD 24(R9), R2 + MOVD 32(R9), R3 + MOVD 80(R9), R5 + MOVD 104(R9), R6 + MOVD 120(R9), R7 + MOVD 56(R9), R8 + MOVD 64(R9), R9 + ADD R9, R8, R8 + + // seqsBase += 24 * seqIndex + ADD R2<<1, R2, R10 + LSL $0x03, R10, R10 + ADD R10, R0, R0 + + // outBase += outPosition + ADD R6, R3, R3 + +main_loop: + MOVD (R0), R10 + MOVD 16(R0), R11 + MOVD 8(R0), R12 + + // Copy literals + TST R10, R10 + BEQ check_offset + MOVD $0, R13 + +copy_1: + ADD R13, R5, R15 + VLD1 (R15), [V0.B16] + ADD R13, R3, R15 + VST1 [V0.B16], (R15) + ADD $0x10, R13, R13 + CMP R10, R13 + BLO copy_1 + ADD R10, R5, R5 + ADD R10, R3, R3 + ADD R10, R6, R6 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + ADD R9, R6, R10 + CMP R10, R11 + BGT error_match_off_too_big + CMP R7, R11 + BGT error_match_off_too_big + + // Copy match from history + MOVD R11, R10 + SUBS R6, R10, R10 + BLS copy_match + MOVD R8, R13 + SUB R10, R13, R13 + CMP R10, R12 + BGT copy_all_from_history + MOVD R12, R10 + SUBS $0x10, R10, R10 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVHU (R13), R10 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R11 + MOVH R10, (R3) + MOVB R11, 2(R3) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R10 + ADD R12, R13, R15 + MOVWU -4(R15), R11 + MOVW R10, (R3) + ADD R12, R3, R15 + MOVW R11, -4(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R10 + ADD R12, R13, R15 + MOVD -8(R15), R11 + MOVD R10, (R3) + ADD R12, R3, R15 + MOVD R11, -8(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + +copy_4_end: + ADD R12, R6, R6 + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + JMP loop_finished + +copy_all_from_history: + MOVD R10, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R10 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R10 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVBU (R13), R16 + BFI $0, R16, $8, R14 + ADD R10, R13, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R4 + MOVB R14, (R3) + ADD R10, R3, R15 + MOVB R4, -1(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_3: + MOVHU (R13), R14 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R4 + MOVH R14, (R3) + MOVB R4, 2(R3) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R10, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R3) + ADD R10, R3, R15 + MOVW R4, -4(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R10, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R3) + ADD R10, R3, R15 + MOVD R4, -8(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + +copy_5_end: + ADD R10, R6, R6 + SUB R10, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R3, R10 + SUB R11, R10, R10 + + // ml <= mo + CMP R11, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R6, R6 + MOVD R3, R11 + ADD R12, R3, R3 + +copy_2: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R11) + ADD $0x10, R10, R10 + ADD $0x10, R11, R11 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R6, R6 + +copy_slow_3: + MOVBU (R10), R16 + BFI $0, R16, $8, R11 + MOVB R11, (R3) + ADD $1, R10, R10 + ADD $1, R3, R3 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + +loop_finished: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +error_match_off_too_big: + // Return value + MOVD $0x00, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +empty_seqs: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + RET + +// func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_safe_arm64(SB), $8-9 + MOVD ctx+0(FP), R9 + MOVD 8(R9), R1 + TST R1, R1 + BEQ empty_seqs + MOVD (R9), R0 + MOVD 24(R9), R2 + MOVD 32(R9), R3 + MOVD 80(R9), R5 + MOVD 104(R9), R6 + MOVD 120(R9), R7 + MOVD 56(R9), R8 + MOVD 64(R9), R9 + ADD R9, R8, R8 + + // seqsBase += 24 * seqIndex + ADD R2<<1, R2, R10 + LSL $0x03, R10, R10 + ADD R10, R0, R0 + + // outBase += outPosition + ADD R6, R3, R3 + +main_loop: + MOVD (R0), R10 + MOVD 16(R0), R11 + MOVD 8(R0), R12 + + // Copy literals + TST R10, R10 + BEQ check_offset + MOVD R10, R13 + SUBS $0x10, R13, R13 + BLO copy_1_small + +copy_1_loop: + VLD1 (R5), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R5, R5 + ADD $0x10, R3, R3 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R5, R5 + ADD $16, R5, R5 + ADD R13, R3, R3 + ADD $16, R3, R3 + ADD $-16, R5, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_1_end + +copy_1_small: + CMP $0x03, R10 + BEQ copy_1_move_3 + BLO copy_1_move_1or2 + CMP $0x08, R10 + BLO copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVBU (R5), R16 + BFI $0, R16, $8, R13 + ADD R10, R5, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R14 + MOVB R13, (R3) + ADD R10, R3, R15 + MOVB R14, -1(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_3: + MOVHU (R5), R13 + MOVBU 2(R5), R16 + BFI $0, R16, $8, R14 + MOVH R13, (R3) + MOVB R14, 2(R3) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_4through7: + MOVWU (R5), R13 + ADD R10, R5, R15 + MOVWU -4(R15), R14 + MOVW R13, (R3) + ADD R10, R3, R15 + MOVW R14, -4(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_8through16: + MOVD (R5), R13 + ADD R10, R5, R15 + MOVD -8(R15), R14 + MOVD R13, (R3) + ADD R10, R3, R15 + MOVD R14, -8(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + +copy_1_end: + ADD R10, R6, R6 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + ADD R9, R6, R10 + CMP R10, R11 + BGT error_match_off_too_big + CMP R7, R11 + BGT error_match_off_too_big + + // Copy match from history + MOVD R11, R10 + SUBS R6, R10, R10 + BLS copy_match + MOVD R8, R13 + SUB R10, R13, R13 + CMP R10, R12 + BGT copy_all_from_history + MOVD R12, R10 + SUBS $0x10, R10, R10 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVHU (R13), R10 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R11 + MOVH R10, (R3) + MOVB R11, 2(R3) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R10 + ADD R12, R13, R15 + MOVWU -4(R15), R11 + MOVW R10, (R3) + ADD R12, R3, R15 + MOVW R11, -4(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R10 + ADD R12, R13, R15 + MOVD -8(R15), R11 + MOVD R10, (R3) + ADD R12, R3, R15 + MOVD R11, -8(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + +copy_4_end: + ADD R12, R6, R6 + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + JMP loop_finished + +copy_all_from_history: + MOVD R10, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R10 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R10 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVBU (R13), R16 + BFI $0, R16, $8, R14 + ADD R10, R13, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R4 + MOVB R14, (R3) + ADD R10, R3, R15 + MOVB R4, -1(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_3: + MOVHU (R13), R14 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R4 + MOVH R14, (R3) + MOVB R4, 2(R3) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R10, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R3) + ADD R10, R3, R15 + MOVW R4, -4(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R10, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R3) + ADD R10, R3, R15 + MOVD R4, -8(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + +copy_5_end: + ADD R10, R6, R6 + SUB R10, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R3, R10 + SUB R11, R10, R10 + + // ml <= mo + CMP R11, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R6, R6 + MOVD R12, R11 + SUBS $0x10, R11, R11 + BLO copy_2_small + +copy_2_loop: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R10, R10 + ADD $0x10, R3, R3 + SUBS $0x10, R11, R11 + BHS copy_2_loop + ADD R11, R10, R10 + ADD $16, R10, R10 + ADD R11, R3, R3 + ADD $16, R3, R3 + ADD $-16, R10, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_2_end + +copy_2_small: + CMP $0x03, R12 + BEQ copy_2_move_3 + BLO copy_2_move_1or2 + CMP $0x08, R12 + BLO copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVBU (R10), R16 + BFI $0, R16, $8, R11 + ADD R12, R10, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R13 + MOVB R11, (R3) + ADD R12, R3, R15 + MOVB R13, -1(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_3: + MOVHU (R10), R11 + MOVBU 2(R10), R16 + BFI $0, R16, $8, R13 + MOVH R11, (R3) + MOVB R13, 2(R3) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_4through7: + MOVWU (R10), R11 + ADD R12, R10, R15 + MOVWU -4(R15), R13 + MOVW R11, (R3) + ADD R12, R3, R15 + MOVW R13, -4(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_8through16: + MOVD (R10), R11 + ADD R12, R10, R15 + MOVD -8(R15), R13 + MOVD R11, (R3) + ADD R12, R3, R15 + MOVD R13, -8(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R6, R6 + +copy_slow_3: + MOVBU (R10), R16 + BFI $0, R16, $8, R11 + MOVB R11, (R3) + ADD $1, R10, R10 + ADD $1, R3, R3 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + +loop_finished: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +error_match_off_too_big: + // Return value + MOVD $0x00, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +empty_seqs: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + RET + +// func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_arm64(SB), $64-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, 8(RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD $0, R1 + MOVD R1, 16(RSP) + MOVD R1, 24(RSP) + MOVD R1, 32(RSP) + MOVD 112(R0), R9 + MOVD 128(R0), R1 + MOVD R1, 40(RSP) + MOVD 144(R0), R10 + MOVD 136(R0), R11 + MOVD 200(R0), R1 + MOVD R1, 64(RSP) + MOVD 176(R0), R1 + MOVD R1, 56(RSP) + MOVD 184(R0), R0 + MOVD R0, 48(RSP) + MOVD 48(RSP), R0 + MOVD 56(RSP), R16 + ADD R0, R16, R16 + MOVD R16, 56(RSP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + MOVD 40(RSP), R16 + ADD R9, R16, R16 + MOVD R16, 40(RSP) + + // outBase += outPosition + ADD R11, R9, R9 + +sequenceDecs_decodeSync_amd64_main_loop: + MOVD 8(RSP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_amd64_fill_end + +sequenceDecs_decodeSync_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_of_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_of_update_zero: + MOVD R0, 16(RSP) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_ml_update_zero: + MOVD R0, 24(RSP) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_amd64_fill_2_end + +sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_ll_update_zero: + MOVD R0, 32(RSP) + + // Fill bitreader for state updates + MOVD R12, 8(RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R12 + LSRW $0x10, R6, R6 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + BFI $0, R12, $8, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R12 + LSRW $0x10, R7, R7 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + BFI $0, R12, $8, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R12 + LSRW $0x10, R8, R8 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + BFI $0, R12, $8, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decodeSync_amd64_skip_update: + // Adjust offset + MOVD s+0(FP), R1 + MOVD 16(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 + ADD $144, R1, R15 + VLD1 (R15), [V0.B16] + MOVD R12, 144(R1) + ADD $152, R1, R15 + VST1 [V0.B16], (R15) + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0: + MOVD 32(RSP), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decodeSync_amd64_adjust_offset_maybezero + ADD $1, R12, R12 + JMP sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_amd64_adjust_offset_maybezero: + TST R12, R12 + BNE sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + MOVD 144(R1), R12 + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offset_nonzero: + MOVD R12, R0 + MOVD $0, R13 + MOVD $-1, R14 + CMP $0x03, R12 + CSEL EQ, R13, R0, R0 + CSEL EQ, R14, R13, R13 + ADD R0<<3, R1, R15 + MOVD 144(R15), R16 + ADDS R16, R13, R13 + BNE sequenceDecs_decodeSync_amd64_adjust_temp_valid + MOVD $0x00000001, R13 + +sequenceDecs_decodeSync_amd64_adjust_temp_valid: + CMP $0x01, R12 + BEQ sequenceDecs_decodeSync_amd64_adjust_skip + MOVD 152(R1), R0 + MOVD R0, 160(R1) + +sequenceDecs_decodeSync_amd64_adjust_skip: + MOVD 144(R1), R0 + MOVD R0, 152(R1) + MOVD R13, 144(R1) + MOVD R13, R12 + +sequenceDecs_decodeSync_amd64_after_adjust: + MOVD R12, 16(RSP) + + // Check values + MOVD 24(RSP), R0 + MOVD 32(RSP), R1 + ADD R1, R0, R13 + MOVD s+0(FP), R14 + MOVD 256(R14), R16 + ADD R13, R16, R16 + MOVD R16, 256(R14) + MOVD ctx+16(FP), R13 + MOVD 104(R13), R16 + SUBS R1, R16, R16 + MOVD R16, 104(R13) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decodeSync_amd64_error_match_len_too_big + TST R12, R12 + BNE sequenceDecs_decodeSync_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_amd64_match_len_ofs_ok: + MOVD 32(RSP), R0 + MOVD 16(RSP), R1 + MOVD 24(RSP), R12 + + // Check if we have enough space in s.out + ADD R12, R0, R13 + ADD $16, R13, R13 + ADD R9, R13, R13 + MOVD 40(RSP), R16 + CMP R16, R13 + BHI error_not_enough_space + + // Copy literals + TST R0, R0 + BEQ check_offset + MOVD $0, R13 + +copy_1: + ADD R13, R10, R15 + VLD1 (R15), [V0.B16] + ADD R13, R9, R15 + VST1 [V0.B16], (R15) + ADD $0x10, R13, R13 + CMP R0, R13 + BLO copy_1 + ADD R0, R10, R10 + ADD R0, R9, R9 + ADD R0, R11, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVD R11, R0 + MOVD 48(RSP), R16 + ADD R16, R0, R0 + CMP R0, R1 + BGT error_match_off_too_big + MOVD 64(RSP), R16 + CMP R16, R1 + BGT error_match_off_too_big + + // Copy match from history + MOVD R1, R0 + SUBS R11, R0, R0 + BLS copy_match + MOVD 56(RSP), R13 + SUB R0, R13, R13 + CMP R0, R12 + BGT copy_all_from_history + MOVD R12, R0 + SUBS $0x10, R0, R0 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVHU (R13), R0 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R1 + MOVH R0, (R9) + MOVB R1, 2(R9) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R0 + ADD R12, R13, R15 + MOVWU -4(R15), R1 + MOVW R0, (R9) + ADD R12, R9, R15 + MOVW R1, -4(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R0 + ADD R12, R13, R15 + MOVD -8(R15), R1 + MOVD R0, (R9) + ADD R12, R9, R15 + MOVD R1, -8(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + +copy_4_end: + ADD R12, R11, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVD R0, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R0 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R0 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVBU (R13), R16 + BFI $0, R16, $8, R14 + ADD R0, R13, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R4 + MOVB R14, (R9) + ADD R0, R9, R15 + MOVB R4, -1(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_3: + MOVHU (R13), R14 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R4 + MOVH R14, (R9) + MOVB R4, 2(R9) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R0, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R9) + ADD R0, R9, R15 + MOVW R4, -4(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R0, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R9) + ADD R0, R9, R15 + MOVD R4, -8(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + +copy_5_end: + ADD R0, R11, R11 + SUB R0, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R9, R0 + SUB R1, R0, R0 + + // ml <= mo + CMP R1, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R11, R11 + MOVD R9, R1 + ADD R12, R9, R9 + +copy_2: + VLD1 (R0), [V0.B16] + VST1 [V0.B16], (R1) + ADD $0x10, R0, R0 + ADD $0x10, R1, R1 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R11, R11 + +copy_slow_3: + MOVBU (R0), R16 + BFI $0, R16, $8, R1 + MOVB R1, (R9) + ADD $1, R0, R0 + ADD $1, R9, R9 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decodeSync_amd64_main_loop + +loop_finished: + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Update the context + MOVD ctx+16(FP), R0 + MOVD R11, 136(R0) + MOVD 144(R0), R1 + SUB R1, R10, R10 + MOVD R10, 168(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch: + MOVD 24(RSP), R0 + MOVD ctx+16(FP), R1 + MOVD R0, 216(R1) + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_amd64_error_match_len_too_big: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 216(R0) + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVD ctx+16(FP), R0 + MOVD 16(RSP), R1 + MOVD R1, 224(R0) + MOVD R11, 136(R0) + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD ctx+16(FP), R0 + MOVD 32(RSP), R1 + MOVD R1, 208(R0) + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVD ctx+16(FP), R0 + MOVD 32(RSP), R1 + MOVD R1, 208(R0) + MOVD 24(RSP), R1 + MOVD R1, 216(R0) + MOVD R11, 136(R0) + MOVD $0x00000005, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decodeSync_bmi2 (generic twin preferred on arm64) + +// func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_safe_arm64(SB), $64-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, 8(RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD $0, R1 + MOVD R1, 16(RSP) + MOVD R1, 24(RSP) + MOVD R1, 32(RSP) + MOVD 112(R0), R9 + MOVD 128(R0), R1 + MOVD R1, 40(RSP) + MOVD 144(R0), R10 + MOVD 136(R0), R11 + MOVD 200(R0), R1 + MOVD R1, 64(RSP) + MOVD 176(R0), R1 + MOVD R1, 56(RSP) + MOVD 184(R0), R0 + MOVD R0, 48(RSP) + MOVD 48(RSP), R0 + MOVD 56(RSP), R16 + ADD R0, R16, R16 + MOVD R16, 56(RSP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + MOVD 40(RSP), R16 + ADD R9, R16, R16 + MOVD R16, 40(RSP) + + // outBase += outPosition + ADD R11, R9, R9 + +sequenceDecs_decodeSync_safe_amd64_main_loop: + MOVD 8(RSP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_safe_amd64_fill_end + +sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_safe_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_safe_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_of_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_of_update_zero: + MOVD R0, 16(RSP) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_ml_update_zero: + MOVD R0, 24(RSP) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_end + +sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_safe_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R16 + BFI $0, R16, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_ll_update_zero: + MOVD R0, 32(RSP) + + // Fill bitreader for state updates + MOVD R12, 8(RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_safe_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R12 + LSRW $0x10, R6, R6 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + BFI $0, R12, $8, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R12 + LSRW $0x10, R7, R7 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + BFI $0, R12, $8, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R12 + LSRW $0x10, R8, R8 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + BFI $0, R12, $8, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decodeSync_safe_amd64_skip_update: + // Adjust offset + MOVD s+0(FP), R1 + MOVD 16(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 + ADD $144, R1, R15 + VLD1 (R15), [V0.B16] + MOVD R12, 144(R1) + ADD $152, R1, R15 + VST1 [V0.B16], (R15) + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0: + MOVD 32(RSP), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero + ADD $1, R12, R12 + JMP sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero: + TST R12, R12 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + MOVD 144(R1), R12 + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero: + MOVD R12, R0 + MOVD $0, R13 + MOVD $-1, R14 + CMP $0x03, R12 + CSEL EQ, R13, R0, R0 + CSEL EQ, R14, R13, R13 + ADD R0<<3, R1, R15 + MOVD 144(R15), R16 + ADDS R16, R13, R13 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid + MOVD $0x00000001, R13 + +sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid: + CMP $0x01, R12 + BEQ sequenceDecs_decodeSync_safe_amd64_adjust_skip + MOVD 152(R1), R0 + MOVD R0, 160(R1) + +sequenceDecs_decodeSync_safe_amd64_adjust_skip: + MOVD 144(R1), R0 + MOVD R0, 152(R1) + MOVD R13, 144(R1) + MOVD R13, R12 + +sequenceDecs_decodeSync_safe_amd64_after_adjust: + MOVD R12, 16(RSP) + + // Check values + MOVD 24(RSP), R0 + MOVD 32(RSP), R1 + ADD R1, R0, R13 + MOVD s+0(FP), R14 + MOVD 256(R14), R16 + ADD R13, R16, R16 + MOVD R16, 256(R14) + MOVD ctx+16(FP), R13 + MOVD 104(R13), R16 + SUBS R1, R16, R16 + MOVD R16, 104(R13) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big + TST R12, R12 + BNE sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok: + MOVD 32(RSP), R0 + MOVD 16(RSP), R1 + MOVD 24(RSP), R12 + + // Check if we have enough space in s.out + ADD R12, R0, R13 + ADD R9, R13, R13 + MOVD 40(RSP), R16 + CMP R16, R13 + BHI error_not_enough_space + + // Copy literals + TST R0, R0 + BEQ check_offset + MOVD R0, R13 + SUBS $0x10, R13, R13 + BLO copy_1_small + +copy_1_loop: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R10, R10 + ADD $0x10, R9, R9 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R10, R10 + ADD $16, R10, R10 + ADD R13, R9, R9 + ADD $16, R9, R9 + ADD $-16, R10, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_1_end + +copy_1_small: + CMP $0x03, R0 + BEQ copy_1_move_3 + BLO copy_1_move_1or2 + CMP $0x08, R0 + BLO copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVBU (R10), R16 + BFI $0, R16, $8, R13 + ADD R0, R10, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R14 + MOVB R13, (R9) + ADD R0, R9, R15 + MOVB R14, -1(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_3: + MOVHU (R10), R13 + MOVBU 2(R10), R16 + BFI $0, R16, $8, R14 + MOVH R13, (R9) + MOVB R14, 2(R9) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_4through7: + MOVWU (R10), R13 + ADD R0, R10, R15 + MOVWU -4(R15), R14 + MOVW R13, (R9) + ADD R0, R9, R15 + MOVW R14, -4(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_8through16: + MOVD (R10), R13 + ADD R0, R10, R15 + MOVD -8(R15), R14 + MOVD R13, (R9) + ADD R0, R9, R15 + MOVD R14, -8(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + +copy_1_end: + ADD R0, R11, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVD R11, R0 + MOVD 48(RSP), R16 + ADD R16, R0, R0 + CMP R0, R1 + BGT error_match_off_too_big + MOVD 64(RSP), R16 + CMP R16, R1 + BGT error_match_off_too_big + + // Copy match from history + MOVD R1, R0 + SUBS R11, R0, R0 + BLS copy_match + MOVD 56(RSP), R13 + SUB R0, R13, R13 + CMP R0, R12 + BGT copy_all_from_history + MOVD R12, R0 + SUBS $0x10, R0, R0 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVHU (R13), R0 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R1 + MOVH R0, (R9) + MOVB R1, 2(R9) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R0 + ADD R12, R13, R15 + MOVWU -4(R15), R1 + MOVW R0, (R9) + ADD R12, R9, R15 + MOVW R1, -4(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R0 + ADD R12, R13, R15 + MOVD -8(R15), R1 + MOVD R0, (R9) + ADD R12, R9, R15 + MOVD R1, -8(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + +copy_4_end: + ADD R12, R11, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVD R0, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R0 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R0 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVBU (R13), R16 + BFI $0, R16, $8, R14 + ADD R0, R13, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R4 + MOVB R14, (R9) + ADD R0, R9, R15 + MOVB R4, -1(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_3: + MOVHU (R13), R14 + MOVBU 2(R13), R16 + BFI $0, R16, $8, R4 + MOVH R14, (R9) + MOVB R4, 2(R9) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R0, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R9) + ADD R0, R9, R15 + MOVW R4, -4(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R0, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R9) + ADD R0, R9, R15 + MOVD R4, -8(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + +copy_5_end: + ADD R0, R11, R11 + SUB R0, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R9, R0 + SUB R1, R0, R0 + + // ml <= mo + CMP R1, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R11, R11 + MOVD R12, R1 + SUBS $0x10, R1, R1 + BLO copy_2_small + +copy_2_loop: + VLD1 (R0), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R0, R0 + ADD $0x10, R9, R9 + SUBS $0x10, R1, R1 + BHS copy_2_loop + ADD R1, R0, R0 + ADD $16, R0, R0 + ADD R1, R9, R9 + ADD $16, R9, R9 + ADD $-16, R0, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_2_end + +copy_2_small: + CMP $0x03, R12 + BEQ copy_2_move_3 + BLO copy_2_move_1or2 + CMP $0x08, R12 + BLO copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVBU (R0), R16 + BFI $0, R16, $8, R1 + ADD R12, R0, R15 + MOVBU -1(R15), R16 + BFI $0, R16, $8, R13 + MOVB R1, (R9) + ADD R12, R9, R15 + MOVB R13, -1(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_3: + MOVHU (R0), R1 + MOVBU 2(R0), R16 + BFI $0, R16, $8, R13 + MOVH R1, (R9) + MOVB R13, 2(R9) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_4through7: + MOVWU (R0), R1 + ADD R12, R0, R15 + MOVWU -4(R15), R13 + MOVW R1, (R9) + ADD R12, R9, R15 + MOVW R13, -4(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_8through16: + MOVD (R0), R1 + ADD R12, R0, R15 + MOVD -8(R15), R13 + MOVD R1, (R9) + ADD R12, R9, R15 + MOVD R13, -8(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R11, R11 + +copy_slow_3: + MOVBU (R0), R16 + BFI $0, R16, $8, R1 + MOVB R1, (R9) + ADD $1, R0, R0 + ADD $1, R9, R9 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decodeSync_safe_amd64_main_loop + +loop_finished: + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Update the context + MOVD ctx+16(FP), R0 + MOVD R11, 136(R0) + MOVD 144(R0), R1 + SUB R1, R10, R10 + MOVD R10, 168(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch: + MOVD 24(RSP), R0 + MOVD ctx+16(FP), R1 + MOVD R0, 216(R1) + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 216(R0) + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVD ctx+16(FP), R0 + MOVD 16(RSP), R1 + MOVD R1, 224(R0) + MOVD R11, 136(R0) + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD ctx+16(FP), R0 + MOVD 32(RSP), R1 + MOVD R1, 208(R0) + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVD ctx+16(FP), R0 + MOVD 32(RSP), R1 + MOVD R1, 208(R0) + MOVD 24(RSP), R1 + MOVD R1, 216(R0) + MOVD R11, 136(R0) + MOVD $0x00000005, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decodeSync_safe_bmi2 (generic twin preferred on arm64) diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go b/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go new file mode 100644 index 0000000000..42ce5de716 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go @@ -0,0 +1,316 @@ +//go:build (amd64 || arm64) && !appengine && !noasm && gc + +package zstd + +import ( + "fmt" + "io" +) + +// This file holds the parts of the assembly sequence decoder that are identical +// across architectures: the context structs exchanged with the asm, the error +// codes, and the decode/decodeSync/executeSimple wrappers. Each architecture +// supplies the small dispatch helpers (decodeAsm, decodeSyncAsm, +// executeSimpleAsm) that select the concrete asm routine — amd64 also chooses a +// BMI2 variant, arm64 has a single implementation. + +type decodeSyncAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + litRemain int + out []byte + outPosition int + literals []byte + litPosition int + history []byte + windowSize int + ll int // set on error (not for all errors, please refer to _generate/gen.go) + ml int // set on error (not for all errors, please refer to _generate/gen.go) + mo int // set on error (not for all errors, please refer to _generate/gen.go) +} + +type decodeAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + seqs []seqVals + litRemain int +} + +type executeAsmContext struct { + seqs []seqVals + seqIndex int + out []byte + history []byte + literals []byte + outPosition int + litPosition int + windowSize int +} + +const noError = 0 + +// error reported when mo == 0 && ml > 0 +const errorMatchLenOfsMismatch = 1 + +// error reported when ml > maxMatchLen +const errorMatchLenTooBig = 2 + +// error reported when mo > available history or mo > s.windowSize +const errorMatchOffTooBig = 3 + +// error reported when the sum of literal lengths exeeceds the literal buffer size +const errorNotEnoughLiterals = 4 + +// error reported when capacity of `out` is too small +const errorNotEnoughSpace = 5 + +// error reported when bits are overread. +const errorOverread = 6 + +// useSafeDecodeSync reports whether decodeSyncSimple must use the bounds-exact +// ("safe") copy variants for the current buffer geometry. +// +// When the output and literal buffers have compressedBlockOverAlloc (16) +// bytes of slack past their logical use, the assembly may use extended +// memory copies that read and write in 16-byte blocks, overrunning the end +// of a literal run or match by up to 15 bytes. Otherwise it must use the +// bounds-exact ("safe") copies. This mirrors the analogous, always-dynamic +// selection in executeSimple below. +// +// The unsafe copies were disabled in #644 (2022) as a mitigation for a +// crash, but that crash's root cause — an unguarded bitReader overread that +// produced out-of-range match offsets/lengths — was fixed three days later +// in #645, which also added the fuzz corpus that has guarded this path since. +// See #1168. An asan-instrumented fuzz job (see .github/workflows/go.yml) +// covers the extended-copy path, which -race and plain fuzzing cannot. +func (s *sequenceDecs) useSafeDecodeSync() bool { + if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc { + return true + } + if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) { + return true + } + if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { + return true + } + return false +} + +// decode sequences from the stream with the provided history but without a dictionary. +func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { + if len(s.dict) > 0 { + return false, nil + } + if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { + return false, nil + } + + useSafe := s.useSafeDecodeSync() + + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeSyncAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + iteration: s.nSeqs - 1, + litRemain: len(s.literals), + out: s.out, + outPosition: len(s.out), + literals: s.literals, + windowSize: s.windowSize, + history: hist, + } + + s.seqSize = 0 + startSize := len(s.out) + + errCode := decodeSyncAsm(s, br, &ctx, useSafe) + switch errCode { + case noError: + break + + case errorMatchLenOfsMismatch: + return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) + + case errorMatchLenTooBig: + return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) + + case errorMatchOffTooBig: + return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", + ctx.mo, ctx.outPosition+len(hist)-startSize) + + case errorNotEnoughLiterals: + return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", + ctx.ll, ctx.litRemain+ctx.ll) + + case errorOverread: + return true, io.ErrUnexpectedEOF + + case errorNotEnoughSpace: + size := ctx.outPosition + ctx.ll + ctx.ml + if debugDecoder { + println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) + } + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + + default: + return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + return true, err + } + + s.literals = s.literals[ctx.litPosition:] + t := ctx.outPosition + s.out = s.out[:t] + + // Add final literals + s.out = append(s.out, s.literals...) + if debugDecoder { + t += len(s.literals) + if t != len(s.out) { + panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) + } + } + + return true, nil +} + +// decode sequences from the stream without the provided history. +func (s *sequenceDecs) decode(seqs []seqVals) error { + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + seqs: seqs, + iteration: len(seqs) - 1, + litRemain: len(s.literals), + } + + if debugDecoder { + println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") + } + + s.seqSize = 0 + lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 + errCode := decodeAsm(s, br, &ctx, lte56bits) + if errCode != 0 { + i := len(seqs) - ctx.iteration - 1 + switch errCode { + case errorMatchLenOfsMismatch: + ml := ctx.seqs[i].ml + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + + case errorMatchLenTooBig: + ml := ctx.seqs[i].ml + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) + + case errorNotEnoughLiterals: + ll := ctx.seqs[i].ll + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) + case errorOverread: + return io.ErrUnexpectedEOF + } + + return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) + } + + if ctx.litRemain < 0 { + return fmt.Errorf("literal count is too big: total available %d, total requested %d", + len(s.literals), len(s.literals)-ctx.litRemain) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + if debugDecoder { + println("decode: ", br.remain(), "bits remain on stream. code:", errCode) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + } + return err +} + +// executeSimple handles cases when dictionary is not used. +func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { + // Ensure we have enough output size... + if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { + addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] + } + + if debugDecoder { + printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) + } + + var t = len(s.out) + out := s.out[:t+s.seqSize] + + ctx := executeAsmContext{ + seqs: seqs, + seqIndex: 0, + out: out, + history: hist, + outPosition: t, + litPosition: 0, + literals: s.literals, + windowSize: s.windowSize, + } + // useSafe avoids overwriting the output buffer when the literals slice has + // not been allocated with the required over-allocation slack. + useSafe := cap(s.literals) < len(s.literals)+compressedBlockOverAlloc + + ok := executeSimpleAsm(&ctx, useSafe) + if !ok { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", + seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) + } + s.literals = s.literals[ctx.litPosition:] + t = ctx.outPosition + + // Add final literals + copy(out[t:], s.literals) + if debugDecoder { + t += len(s.literals) + if t != len(out) { + panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) + } + } + s.out = out + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go index 516cd9b070..8a3db6ba22 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/snappy.go b/vendor/github.com/klauspost/compress/zstd/snappy.go index 336c288930..36c56f36d7 100644 --- a/vendor/github.com/klauspost/compress/zstd/snappy.go +++ b/vendor/github.com/klauspost/compress/zstd/snappy.go @@ -334,9 +334,10 @@ func decodeSnappy(blk *blockEnc, src []byte) error { return errUnsupportedLiteralLength } - //if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s { - // return ErrSnappyCorrupt - //} + if length > len(src)-s { + println("length > len(src)-s", length, len(src)-s) + return ErrSnappyCorrupt + } blk.literals = append(blk.literals, src[s:s+length]...) //println(length, "litLen") diff --git a/vendor/modules.txt b/vendor/modules.txt index 1cebccd619..1fbb530bde 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -146,7 +146,7 @@ github.com/grpc-ecosystem/grpc-gateway/v2/utilities # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap -# github.com/klauspost/compress v1.18.7 +# github.com/klauspost/compress v1.19.2 ## explicit; go 1.24 github.com/klauspost/compress github.com/klauspost/compress/fse From 8eb66b47e3751f9af69afb3bc08b08fb00349023 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 28 Aug 2026 20:46:02 +0200 Subject: [PATCH 09/44] update minimum go version to go1.26 Signed-off-by: Sebastiaan van Stijn --- cli-plugins/hooks/hook_types.go | 2 +- cli-plugins/hooks/template.go | 2 +- cli-plugins/manager/error.go | 2 +- cli-plugins/manager/hooks.go | 2 +- cli/command/cli.go | 2 +- cli/command/config/formatter.go | 2 +- cli/command/config/inspect.go | 2 +- cli/command/container/completion.go | 2 +- cli/command/container/inspect.go | 2 +- cli/command/container/opts.go | 2 +- cli/command/container/stats.go | 2 +- cli/command/container/tty.go | 2 +- cli/command/context.go | 2 +- cli/command/context/completion.go | 2 +- cli/command/context/create.go | 2 +- cli/command/context/create_test.go | 2 +- cli/command/context/inspect.go | 2 +- cli/command/context/list.go | 2 +- cli/command/context_test.go | 2 +- cli/command/defaultcontextstore.go | 2 +- cli/command/defaultcontextstore_test.go | 2 +- cli/command/formatter/container.go | 2 +- cli/command/formatter/container_test.go | 2 +- cli/command/formatter/custom.go | 2 +- cli/command/formatter/displayutils.go | 2 +- cli/command/formatter/formatter.go | 2 +- cli/command/formatter/formatter_test.go | 2 +- cli/command/formatter/reflect.go | 2 +- cli/command/formatter/reflect_test.go | 2 +- cli/command/formatter/volume.go | 2 +- cli/command/formatter/volume_test.go | 2 +- cli/command/idresolver/idresolver.go | 2 +- cli/command/image/inspect.go | 2 +- cli/command/image/list.go | 2 +- cli/command/image/push.go | 2 +- cli/command/image/tree.go | 2 +- cli/command/inspect/inspector.go | 2 +- cli/command/manifest/annotate.go | 2 +- cli/command/network/formatter.go | 2 +- cli/command/network/formatter_test.go | 2 +- cli/command/network/inspect.go | 2 +- cli/command/node/formatter_test.go | 2 +- cli/command/node/inspect.go | 2 +- cli/command/node/update.go | 2 +- cli/command/plugin/formatter_test.go | 2 +- cli/command/plugin/inspect.go | 2 +- cli/command/secret/formatter.go | 2 +- cli/command/secret/inspect.go | 2 +- cli/command/service/formatter_test.go | 2 +- cli/command/service/inspect.go | 2 +- cli/command/service/inspect_test.go | 2 +- cli/command/service/internal/genericresource/parse.go | 2 +- cli/command/service/opts.go | 2 +- cli/command/service/progress/progress.go | 2 +- cli/command/service/update.go | 2 +- cli/command/stack/loader.go | 2 +- cli/command/system/info.go | 2 +- cli/command/system/inspect.go | 2 +- cli/command/system/prune.go | 2 +- cli/command/system/pruner/pruner.go | 2 +- cli/command/telemetry_docker.go | 2 +- cli/command/utils.go | 2 +- cli/command/volume/create.go | 2 +- cli/command/volume/create_test.go | 2 +- cli/command/volume/inspect.go | 2 +- cli/compose/convert/service.go | 2 +- cli/compose/interpolation/interpolation.go | 2 +- cli/compose/interpolation/interpolation_test.go | 2 +- cli/compose/loader/full-struct_test.go | 2 +- cli/compose/loader/interpolate.go | 2 +- cli/compose/loader/loader.go | 2 +- cli/compose/loader/loader_test.go | 2 +- cli/compose/loader/merge.go | 2 +- cli/compose/loader/merge_test.go | 2 +- cli/compose/schema/schema.go | 2 +- cli/compose/schema/schema_test.go | 2 +- cli/compose/template/template.go | 2 +- cli/compose/template/template_test.go | 2 +- cli/compose/types/types.go | 2 +- cli/config/configfile/file.go | 2 +- cli/config/memorystore/store.go | 2 +- cli/connhelper/connhelper.go | 2 +- cli/context/store/metadata_test.go | 2 +- cli/context/store/metadatastore.go | 2 +- cli/context/store/store.go | 2 +- cli/context/store/store_test.go | 2 +- cli/context/store/storeconfig.go | 2 +- cli/context/store/storeconfig_test.go | 2 +- cli/streams/stream.go | 2 +- cmd/docker-trust/internal/registry/config.go | 2 +- cmd/docker-trust/internal/registry/errors.go | 2 +- cmd/docker-trust/trust/inspect.go | 2 +- cmd/docker/builder_test.go | 2 +- cmd/docker/docker.go | 2 +- internal/oauth/api/api.go | 2 +- internal/registry/config.go | 2 +- internal/registry/errors.go | 2 +- internal/test/strings.go | 2 +- internal/tui/chip.go | 2 +- internal/tui/colors.go | 2 +- internal/tui/count.go | 2 +- internal/tui/note.go | 2 +- internal/tui/output.go | 2 +- internal/tui/str.go | 2 +- internal/volumespec/volumespec.go | 2 +- opts/opts.go | 2 +- opts/swarmopts/port.go | 2 +- opts/swarmopts/port_test.go | 2 +- templates/templates.go | 2 +- vendor.mod | 2 +- 110 files changed, 110 insertions(+), 110 deletions(-) diff --git a/cli-plugins/hooks/hook_types.go b/cli-plugins/hooks/hook_types.go index c2b0b3ba4f..03ee729493 100644 --- a/cli-plugins/hooks/hook_types.go +++ b/cli-plugins/hooks/hook_types.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 // Package hooks defines the contract between the Docker CLI and CLI plugin hook // implementations. diff --git a/cli-plugins/hooks/template.go b/cli-plugins/hooks/template.go index eca5ce4f72..99abb9acf8 100644 --- a/cli-plugins/hooks/template.go +++ b/cli-plugins/hooks/template.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package hooks diff --git a/cli-plugins/manager/error.go b/cli-plugins/manager/error.go index 6dd06405d6..5e66078b64 100644 --- a/cli-plugins/manager/error.go +++ b/cli-plugins/manager/error.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package manager diff --git a/cli-plugins/manager/hooks.go b/cli-plugins/manager/hooks.go index 9bbacb1c9c..5a3d3ebe91 100644 --- a/cli-plugins/manager/hooks.go +++ b/cli-plugins/manager/hooks.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package manager diff --git a/cli/command/cli.go b/cli/command/cli.go index eb69b3eb73..f7f428b11b 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/config/formatter.go b/cli/command/config/formatter.go index 8e0b296df7..9453dec77e 100644 --- a/cli/command/config/formatter.go +++ b/cli/command/config/formatter.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package config diff --git a/cli/command/config/inspect.go b/cli/command/config/inspect.go index 1991922ed8..a7ac662900 100644 --- a/cli/command/config/inspect.go +++ b/cli/command/config/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package config diff --git a/cli/command/container/completion.go b/cli/command/container/completion.go index 7d4940ad26..97d32728fa 100644 --- a/cli/command/container/completion.go +++ b/cli/command/container/completion.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package container diff --git a/cli/command/container/inspect.go b/cli/command/container/inspect.go index df01341955..9ebd45016c 100644 --- a/cli/command/container/inspect.go +++ b/cli/command/container/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package container diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 1af0b64546..680a84a8b5 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package container diff --git a/cli/command/container/stats.go b/cli/command/container/stats.go index 9d4bfce51b..c0370f95e5 100644 --- a/cli/command/container/stats.go +++ b/cli/command/container/stats.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package container diff --git a/cli/command/container/tty.go b/cli/command/container/tty.go index 3bf03a9906..bdb4308bdc 100644 --- a/cli/command/container/tty.go +++ b/cli/command/container/tty.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package container diff --git a/cli/command/context.go b/cli/command/context.go index b876e225ef..153b046f57 100644 --- a/cli/command/context.go +++ b/cli/command/context.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/context/completion.go b/cli/command/context/completion.go index aba7d5b74b..f5ea6ad30c 100644 --- a/cli/command/context/completion.go +++ b/cli/command/context/completion.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package context diff --git a/cli/command/context/create.go b/cli/command/context/create.go index f7e62205ba..81e4418939 100644 --- a/cli/command/context/create.go +++ b/cli/command/context/create.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package context diff --git a/cli/command/context/create_test.go b/cli/command/context/create_test.go index a9d64e1248..4dc62b215f 100644 --- a/cli/command/context/create_test.go +++ b/cli/command/context/create_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package context diff --git a/cli/command/context/inspect.go b/cli/command/context/inspect.go index 4e9ab28222..be1300f0a8 100644 --- a/cli/command/context/inspect.go +++ b/cli/command/context/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package context diff --git a/cli/command/context/list.go b/cli/command/context/list.go index d0236d814f..67e9b9c660 100644 --- a/cli/command/context/list.go +++ b/cli/command/context/list.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package context diff --git a/cli/command/context_test.go b/cli/command/context_test.go index 421102d242..993d79a92e 100644 --- a/cli/command/context_test.go +++ b/cli/command/context_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/defaultcontextstore.go b/cli/command/defaultcontextstore.go index 54ca58d2d8..90be220155 100644 --- a/cli/command/defaultcontextstore.go +++ b/cli/command/defaultcontextstore.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/defaultcontextstore_test.go b/cli/command/defaultcontextstore_test.go index 6a4f0866f2..3a7ef5a384 100644 --- a/cli/command/defaultcontextstore_test.go +++ b/cli/command/defaultcontextstore_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/formatter/container.go b/cli/command/formatter/container.go index a282921a51..b65209c26c 100644 --- a/cli/command/formatter/container.go +++ b/cli/command/formatter/container.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/container_test.go b/cli/command/formatter/container_test.go index 6941d26201..07df93d9ae 100644 --- a/cli/command/formatter/container_test.go +++ b/cli/command/formatter/container_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/custom.go b/cli/command/formatter/custom.go index b845e191fa..80a716230b 100644 --- a/cli/command/formatter/custom.go +++ b/cli/command/formatter/custom.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/displayutils.go b/cli/command/formatter/displayutils.go index ef008006ff..d17b432633 100644 --- a/cli/command/formatter/displayutils.go +++ b/cli/command/formatter/displayutils.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/formatter.go b/cli/command/formatter/formatter.go index 01c7867f31..4beb7f56b2 100644 --- a/cli/command/formatter/formatter.go +++ b/cli/command/formatter/formatter.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/formatter_test.go b/cli/command/formatter/formatter_test.go index d07d529a8d..0c1675417d 100644 --- a/cli/command/formatter/formatter_test.go +++ b/cli/command/formatter/formatter_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/reflect.go b/cli/command/formatter/reflect.go index 150c01805a..bb17584209 100644 --- a/cli/command/formatter/reflect.go +++ b/cli/command/formatter/reflect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/reflect_test.go b/cli/command/formatter/reflect_test.go index fdff3f5899..3e1e4405a6 100644 --- a/cli/command/formatter/reflect_test.go +++ b/cli/command/formatter/reflect_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/volume.go b/cli/command/formatter/volume.go index 75b6ad007f..d55d3677cb 100644 --- a/cli/command/formatter/volume.go +++ b/cli/command/formatter/volume.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/formatter/volume_test.go b/cli/command/formatter/volume_test.go index b85a75c387..bcb4227c24 100644 --- a/cli/command/formatter/volume_test.go +++ b/cli/command/formatter/volume_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package formatter diff --git a/cli/command/idresolver/idresolver.go b/cli/command/idresolver/idresolver.go index 95e334535e..2ee6d2c88e 100644 --- a/cli/command/idresolver/idresolver.go +++ b/cli/command/idresolver/idresolver.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package idresolver diff --git a/cli/command/image/inspect.go b/cli/command/image/inspect.go index 714c7c7384..8304740866 100644 --- a/cli/command/image/inspect.go +++ b/cli/command/image/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package image diff --git a/cli/command/image/list.go b/cli/command/image/list.go index 59b639668c..626f06cb84 100644 --- a/cli/command/image/list.go +++ b/cli/command/image/list.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package image diff --git a/cli/command/image/push.go b/cli/command/image/push.go index 78ee48d664..87e2f7dea4 100644 --- a/cli/command/image/push.go +++ b/cli/command/image/push.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package image diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go index 31645ac236..cd053c2f4b 100644 --- a/cli/command/image/tree.go +++ b/cli/command/image/tree.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package image diff --git a/cli/command/inspect/inspector.go b/cli/command/inspect/inspector.go index 63e7b597ba..1f154c088f 100644 --- a/cli/command/inspect/inspector.go +++ b/cli/command/inspect/inspector.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package inspect diff --git a/cli/command/manifest/annotate.go b/cli/command/manifest/annotate.go index 231aa93062..a9507deb73 100644 --- a/cli/command/manifest/annotate.go +++ b/cli/command/manifest/annotate.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package manifest diff --git a/cli/command/network/formatter.go b/cli/command/network/formatter.go index cbc72a7fca..574de2d5f2 100644 --- a/cli/command/network/formatter.go +++ b/cli/command/network/formatter.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package network diff --git a/cli/command/network/formatter_test.go b/cli/command/network/formatter_test.go index 8a9635aefb..6e870373fe 100644 --- a/cli/command/network/formatter_test.go +++ b/cli/command/network/formatter_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package network diff --git a/cli/command/network/inspect.go b/cli/command/network/inspect.go index 77f55d6535..1c27650f0c 100644 --- a/cli/command/network/inspect.go +++ b/cli/command/network/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package network diff --git a/cli/command/node/formatter_test.go b/cli/command/node/formatter_test.go index bd378806ba..a496fafec9 100644 --- a/cli/command/node/formatter_test.go +++ b/cli/command/node/formatter_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package node diff --git a/cli/command/node/inspect.go b/cli/command/node/inspect.go index 9ae166f30b..1020c0eaf9 100644 --- a/cli/command/node/inspect.go +++ b/cli/command/node/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package node diff --git a/cli/command/node/update.go b/cli/command/node/update.go index 542152895c..a5f70e0274 100644 --- a/cli/command/node/update.go +++ b/cli/command/node/update.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package node diff --git a/cli/command/plugin/formatter_test.go b/cli/command/plugin/formatter_test.go index b0098f308d..3c85846054 100644 --- a/cli/command/plugin/formatter_test.go +++ b/cli/command/plugin/formatter_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package plugin diff --git a/cli/command/plugin/inspect.go b/cli/command/plugin/inspect.go index 1b6ec03a6a..ed45605dc1 100644 --- a/cli/command/plugin/inspect.go +++ b/cli/command/plugin/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package plugin diff --git a/cli/command/secret/formatter.go b/cli/command/secret/formatter.go index bc7129960f..9788f1ce1e 100644 --- a/cli/command/secret/formatter.go +++ b/cli/command/secret/formatter.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package secret diff --git a/cli/command/secret/inspect.go b/cli/command/secret/inspect.go index 165645f192..6f614c3d30 100644 --- a/cli/command/secret/inspect.go +++ b/cli/command/secret/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package secret diff --git a/cli/command/service/formatter_test.go b/cli/command/service/formatter_test.go index 4ff8b17186..024c827ec8 100644 --- a/cli/command/service/formatter_test.go +++ b/cli/command/service/formatter_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package service diff --git a/cli/command/service/inspect.go b/cli/command/service/inspect.go index 6ec3a3974b..7e56b317c8 100644 --- a/cli/command/service/inspect.go +++ b/cli/command/service/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package service diff --git a/cli/command/service/inspect_test.go b/cli/command/service/inspect_test.go index 24981ed2f4..2155934cc4 100644 --- a/cli/command/service/inspect_test.go +++ b/cli/command/service/inspect_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package service diff --git a/cli/command/service/internal/genericresource/parse.go b/cli/command/service/internal/genericresource/parse.go index 37305fee2b..9cbaa997af 100644 --- a/cli/command/service/internal/genericresource/parse.go +++ b/cli/command/service/internal/genericresource/parse.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 // Package genericresource is a local fork of SwarmKit's [genericresource] package, // without protobuf dependencies. diff --git a/cli/command/service/opts.go b/cli/command/service/opts.go index da83f81084..e04f392e46 100644 --- a/cli/command/service/opts.go +++ b/cli/command/service/opts.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package service diff --git a/cli/command/service/progress/progress.go b/cli/command/service/progress/progress.go index 36f5d19e46..ef191fce33 100644 --- a/cli/command/service/progress/progress.go +++ b/cli/command/service/progress/progress.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package progress diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 13af4bd746..7520b08891 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package service diff --git a/cli/command/stack/loader.go b/cli/command/stack/loader.go index 60107f7f17..7c535662ce 100644 --- a/cli/command/stack/loader.go +++ b/cli/command/stack/loader.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package stack diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 5ae650a17c..80a21629ec 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package system diff --git a/cli/command/system/inspect.go b/cli/command/system/inspect.go index 76cb4faaae..1912400068 100644 --- a/cli/command/system/inspect.go +++ b/cli/command/system/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package system diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index accbb8558d..698b6d1bb3 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package system diff --git a/cli/command/system/pruner/pruner.go b/cli/command/system/pruner/pruner.go index ab744c1adc..1ff393c5f0 100644 --- a/cli/command/system/pruner/pruner.go +++ b/cli/command/system/pruner/pruner.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 // Package pruner registers "prune" functions to be included as part of // "docker system prune". diff --git a/cli/command/telemetry_docker.go b/cli/command/telemetry_docker.go index 0cd37b3a1c..b262e62e21 100644 --- a/cli/command/telemetry_docker.go +++ b/cli/command/telemetry_docker.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/utils.go b/cli/command/utils.go index bf7638f37b..1bee68cec5 100644 --- a/cli/command/utils.go +++ b/cli/command/utils.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package command diff --git a/cli/command/volume/create.go b/cli/command/volume/create.go index dacd382d2f..5c2098e035 100644 --- a/cli/command/volume/create.go +++ b/cli/command/volume/create.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package volume diff --git a/cli/command/volume/create_test.go b/cli/command/volume/create_test.go index 8992950951..7f8a2c387c 100644 --- a/cli/command/volume/create_test.go +++ b/cli/command/volume/create_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package volume diff --git a/cli/command/volume/inspect.go b/cli/command/volume/inspect.go index 54a9f63ca9..98c1d78475 100644 --- a/cli/command/volume/inspect.go +++ b/cli/command/volume/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package volume diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index cc04bc357a..e23d782c7e 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package convert diff --git a/cli/compose/interpolation/interpolation.go b/cli/compose/interpolation/interpolation.go index 0965b436cf..78dcde79cd 100644 --- a/cli/compose/interpolation/interpolation.go +++ b/cli/compose/interpolation/interpolation.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package interpolation diff --git a/cli/compose/interpolation/interpolation_test.go b/cli/compose/interpolation/interpolation_test.go index 2401824519..a5538836e3 100644 --- a/cli/compose/interpolation/interpolation_test.go +++ b/cli/compose/interpolation/interpolation_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package interpolation diff --git a/cli/compose/loader/full-struct_test.go b/cli/compose/loader/full-struct_test.go index 62defa9cc3..04176bbf5a 100644 --- a/cli/compose/loader/full-struct_test.go +++ b/cli/compose/loader/full-struct_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package loader diff --git a/cli/compose/loader/interpolate.go b/cli/compose/loader/interpolate.go index 3eee05679e..11c44113d4 100644 --- a/cli/compose/loader/interpolate.go +++ b/cli/compose/loader/interpolate.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package loader diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index 0ddebce785..91d85cfd0a 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package loader diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index 549d02ed73..294cec6567 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package loader diff --git a/cli/compose/loader/merge.go b/cli/compose/loader/merge.go index 9d8da3c6d8..4b397a2bd6 100644 --- a/cli/compose/loader/merge.go +++ b/cli/compose/loader/merge.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package loader diff --git a/cli/compose/loader/merge_test.go b/cli/compose/loader/merge_test.go index 11c1e888ea..2f6b456174 100644 --- a/cli/compose/loader/merge_test.go +++ b/cli/compose/loader/merge_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package loader diff --git a/cli/compose/schema/schema.go b/cli/compose/schema/schema.go index 1b21bac8e5..9ea79644e6 100644 --- a/cli/compose/schema/schema.go +++ b/cli/compose/schema/schema.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package schema diff --git a/cli/compose/schema/schema_test.go b/cli/compose/schema/schema_test.go index 36fff864af..cef3d55223 100644 --- a/cli/compose/schema/schema_test.go +++ b/cli/compose/schema/schema_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package schema diff --git a/cli/compose/template/template.go b/cli/compose/template/template.go index 1bceadd9e3..14ba09de35 100644 --- a/cli/compose/template/template.go +++ b/cli/compose/template/template.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package template diff --git a/cli/compose/template/template_test.go b/cli/compose/template/template_test.go index 2286ef7a2f..0b2d463ebb 100644 --- a/cli/compose/template/template_test.go +++ b/cli/compose/template/template_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package template diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 6a7b41fbb5..73f46aae8e 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package types diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index 26e148f059..5edce29158 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package configfile diff --git a/cli/config/memorystore/store.go b/cli/config/memorystore/store.go index 44523d392b..da1e94f416 100644 --- a/cli/config/memorystore/store.go +++ b/cli/config/memorystore/store.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package memorystore diff --git a/cli/connhelper/connhelper.go b/cli/connhelper/connhelper.go index 8d97a2a40b..40281a56f8 100644 --- a/cli/connhelper/connhelper.go +++ b/cli/connhelper/connhelper.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 // Package connhelper provides helpers for connecting to a remote daemon host with custom logic. package connhelper diff --git a/cli/context/store/metadata_test.go b/cli/context/store/metadata_test.go index aff5982092..8c6389894b 100644 --- a/cli/context/store/metadata_test.go +++ b/cli/context/store/metadata_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package store diff --git a/cli/context/store/metadatastore.go b/cli/context/store/metadatastore.go index 8703e1c6c0..8bada79fe5 100644 --- a/cli/context/store/metadatastore.go +++ b/cli/context/store/metadatastore.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package store diff --git a/cli/context/store/store.go b/cli/context/store/store.go index 2b8b5c3114..4122e07792 100644 --- a/cli/context/store/store.go +++ b/cli/context/store/store.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package store diff --git a/cli/context/store/store_test.go b/cli/context/store/store_test.go index 20d0ef6463..25bfd87c75 100644 --- a/cli/context/store/store_test.go +++ b/cli/context/store/store_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package store diff --git a/cli/context/store/storeconfig.go b/cli/context/store/storeconfig.go index 4b42e73779..79fb2da3f9 100644 --- a/cli/context/store/storeconfig.go +++ b/cli/context/store/storeconfig.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package store diff --git a/cli/context/store/storeconfig_test.go b/cli/context/store/storeconfig_test.go index 840d4ee53b..51b5758e94 100644 --- a/cli/context/store/storeconfig_test.go +++ b/cli/context/store/storeconfig_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package store diff --git a/cli/streams/stream.go b/cli/streams/stream.go index f6356ccf34..8a357718d5 100644 --- a/cli/streams/stream.go +++ b/cli/streams/stream.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package streams diff --git a/cmd/docker-trust/internal/registry/config.go b/cmd/docker-trust/internal/registry/config.go index e753fd11af..41f8e4b5f3 100644 --- a/cmd/docker-trust/internal/registry/config.go +++ b/cmd/docker-trust/internal/registry/config.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package registry diff --git a/cmd/docker-trust/internal/registry/errors.go b/cmd/docker-trust/internal/registry/errors.go index a9e75dbdd9..d0bdbe2e83 100644 --- a/cmd/docker-trust/internal/registry/errors.go +++ b/cmd/docker-trust/internal/registry/errors.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package registry diff --git a/cmd/docker-trust/trust/inspect.go b/cmd/docker-trust/trust/inspect.go index c7688e50b2..fabb1d7a13 100644 --- a/cmd/docker-trust/trust/inspect.go +++ b/cmd/docker-trust/trust/inspect.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package trust diff --git a/cmd/docker/builder_test.go b/cmd/docker/builder_test.go index a23b52b922..9f7d67bf47 100644 --- a/cmd/docker/builder_test.go +++ b/cmd/docker/builder_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package main diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 0efcc70118..aca19c76cf 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package main diff --git a/internal/oauth/api/api.go b/internal/oauth/api/api.go index f0d744da3c..9526f84c99 100644 --- a/internal/oauth/api/api.go +++ b/internal/oauth/api/api.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package api diff --git a/internal/registry/config.go b/internal/registry/config.go index 11d6c95625..24b5eedfe3 100644 --- a/internal/registry/config.go +++ b/internal/registry/config.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package registry diff --git a/internal/registry/errors.go b/internal/registry/errors.go index 589831bf05..20a5fca373 100644 --- a/internal/registry/errors.go +++ b/internal/registry/errors.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package registry diff --git a/internal/test/strings.go b/internal/test/strings.go index 0532de8a03..cdf118dc09 100644 --- a/internal/test/strings.go +++ b/internal/test/strings.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package test diff --git a/internal/tui/chip.go b/internal/tui/chip.go index 477d43103b..893046264f 100644 --- a/internal/tui/chip.go +++ b/internal/tui/chip.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package tui diff --git a/internal/tui/colors.go b/internal/tui/colors.go index 854e55ecd0..2b6a0f1f30 100644 --- a/internal/tui/colors.go +++ b/internal/tui/colors.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package tui diff --git a/internal/tui/count.go b/internal/tui/count.go index 303d650e51..05d034808d 100644 --- a/internal/tui/count.go +++ b/internal/tui/count.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package tui diff --git a/internal/tui/note.go b/internal/tui/note.go index 83f1f621e9..1e2f538d97 100644 --- a/internal/tui/note.go +++ b/internal/tui/note.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package tui diff --git a/internal/tui/output.go b/internal/tui/output.go index 1776640af5..9022e66ae8 100644 --- a/internal/tui/output.go +++ b/internal/tui/output.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package tui diff --git a/internal/tui/str.go b/internal/tui/str.go index 9d50f7bd36..819af243cd 100644 --- a/internal/tui/str.go +++ b/internal/tui/str.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package tui diff --git a/internal/volumespec/volumespec.go b/internal/volumespec/volumespec.go index fcc5aa823b..8f497714c8 100644 --- a/internal/volumespec/volumespec.go +++ b/internal/volumespec/volumespec.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package volumespec diff --git a/opts/opts.go b/opts/opts.go index 2e0adac6e0..b944a479d8 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package opts diff --git a/opts/swarmopts/port.go b/opts/swarmopts/port.go index 2ed10bb2dd..f3caf32f46 100644 --- a/opts/swarmopts/port.go +++ b/opts/swarmopts/port.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package swarmopts diff --git a/opts/swarmopts/port_test.go b/opts/swarmopts/port_test.go index 5ccf28052c..dd224f1e4c 100644 --- a/opts/swarmopts/port_test.go +++ b/opts/swarmopts/port_test.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package swarmopts diff --git a/templates/templates.go b/templates/templates.go index e156ca5751..24f29c179f 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -1,5 +1,5 @@ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.25 +//go:build go1.26 package templates diff --git a/vendor.mod b/vendor.mod index 0a21b03055..6d66e1f9c5 100644 --- a/vendor.mod +++ b/vendor.mod @@ -4,7 +4,7 @@ module github.com/docker/cli // There is no 'go.mod' file, as that would imply opting in for all the rules // around SemVer, which this repo cannot abide by as it uses CalVer. -go 1.25.0 +go 1.26 tool ( github.com/cpuguy83/go-md2man/v2 // for scripts/docs/generate-man.sh From be9ca38f8b30f990ccfdacb684d772d16032ecc3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 28 Aug 2026 20:48:48 +0200 Subject: [PATCH 10/44] go fix for go1.26 Signed-off-by: Sebastiaan van Stijn --- cli-plugins/manager/plugin.go | 6 ++-- cli-plugins/plugin/plugin.go | 6 ++-- cli/command/container/start.go | 6 ++-- cli/command/context/options.go | 6 ++-- cli/command/image/build.go | 6 ++-- cli/command/network/create_test.go | 16 ++++----- cli/command/stack/loader.go | 3 +- cli/compose/convert/service.go | 11 ++---- cli/compose/convert/service_test.go | 12 +++---- cli/compose/loader/full-struct_test.go | 38 ++++++++++---------- cli/compose/loader/loader_test.go | 50 +++++++++----------------- cli/compose/loader/merge_test.go | 14 ++++---- cli/compose/schema/schema.go | 3 +- cli/compose/types/types.go | 3 +- cli/config/configfile/file.go | 3 +- cli/connhelper/ssh/ssh.go | 6 ++-- cmd/docker/docker.go | 3 +- internal/registry/errors.go | 3 +- internal/registryclient/fetcher.go | 6 ++-- opts/gpus.go | 6 ++-- opts/network.go | 6 ++-- opts/swarmopts/port.go | 6 ++-- 22 files changed, 99 insertions(+), 120 deletions(-) diff --git a/cli-plugins/manager/plugin.go b/cli-plugins/manager/plugin.go index 4270bac2cc..e0fe504254 100644 --- a/cli-plugins/manager/plugin.go +++ b/cli-plugins/manager/plugin.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package manager import ( @@ -167,8 +170,7 @@ func (p *Plugin) RunHook(ctx context.Context, hookData hooks.Request) ([]byte, e out, err := pCmd.Output() if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { + if _, ok := errors.AsType[*exec.ExitError](err); ok { return nil, wrapAsPluginError(err, "plugin hook subcommand exited unsuccessfully") } return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand: "+pCmd.String()) diff --git a/cli-plugins/plugin/plugin.go b/cli-plugins/plugin/plugin.go index 58ee3b8762..a90e4b6f6c 100644 --- a/cli-plugins/plugin/plugin.go +++ b/cli-plugins/plugin/plugin.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package plugin import ( @@ -97,8 +100,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta metadata.Metadata, ops . plugin := makeCmd(dockerCLI) if err := RunPlugin(dockerCLI, plugin, meta); err != nil { - var stErr cli.StatusError - if errors.As(err, &stErr) { + if stErr, ok := errors.AsType[cli.StatusError](err); ok { // StatusError should only be used for errors, and all errors should // have a non-zero exit status, so never exit with 0 if stErr.StatusCode == 0 { // FIXME(thaJeztah): this should never be used with a zero status-code. Check if we do this anywhere. diff --git a/cli/command/container/start.go b/cli/command/container/start.go index 916f5808d7..f6cdd0c211 100644 --- a/cli/command/container/start.go +++ b/cli/command/container/start.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package container import ( @@ -169,8 +172,7 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er } } if attachErr := <-cErr; attachErr != nil { - var escapeError term.EscapeError - if errors.As(attachErr, &escapeError) { + if _, ok := errors.AsType[term.EscapeError](attachErr); ok { // The user entered the detach escape sequence. return nil } diff --git a/cli/command/context/options.go b/cli/command/context/options.go index 429f47cc41..979b692736 100644 --- a/cli/command/context/options.go +++ b/cli/command/context/options.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package context import ( @@ -69,8 +72,7 @@ func parseBool(config map[string]string, name string) (bool, error) { } res, err := strconv.ParseBool(strVal) if err != nil { - var nErr *strconv.NumError - if errors.As(err, &nErr) { + if nErr, ok := errors.AsType[*strconv.NumError](err); ok { return res, fmt.Errorf("%s: parsing %q: %w", name, nErr.Num, nErr.Err) } return res, fmt.Errorf("%s: %w", name, err) diff --git a/cli/command/image/build.go b/cli/command/image/build.go index db33e0e7c6..26a8739854 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package image import ( @@ -364,8 +367,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) err = jsonstream.Display(ctx, response.Body, streams.NewOut(buildBuff), jsonstream.WithAuxCallback(aux)) if err != nil { - var jerr *jsonstream.JSONError - if errors.As(err, &jerr) { + if jerr, ok := errors.AsType[*jsonstream.JSONError](err); ok { // If no error code is set, default to 1 if jerr.Code == 0 { jerr.Code = 1 diff --git a/cli/command/network/create_test.go b/cli/command/network/create_test.go index 7de25edb63..dc5505023a 100644 --- a/cli/command/network/create_test.go +++ b/cli/command/network/create_test.go @@ -189,8 +189,6 @@ func TestNetworkCreateWithFlags(t *testing.T) { // TestNetworkCreateIPv4 verifies behavior of the "--ipv4" option. This option // is an optional bool, and must default to "nil", not "true" or "false". func TestNetworkCreateIPv4(t *testing.T) { - boolPtr := func(val bool) *bool { return &val } - tests := []struct { doc, name string flags []string @@ -205,19 +203,19 @@ func TestNetworkCreateIPv4(t *testing.T) { doc: "IPv4 enabled", name: "ipv4-enabled", flags: []string{"--ipv4=true"}, - expected: boolPtr(true), + expected: new(true), }, { doc: "IPv4 enabled (shorthand)", name: "ipv4-enabled-shorthand", flags: []string{"--ipv4"}, - expected: boolPtr(true), + expected: new(true), }, { doc: "IPv4 disabled", name: "ipv4-disabled", flags: []string{"--ipv4=false"}, - expected: boolPtr(false), + expected: new(false), }, } @@ -243,8 +241,6 @@ func TestNetworkCreateIPv4(t *testing.T) { // TestNetworkCreateIPv6 verifies behavior of the "--ipv6" option. This option // is an optional bool, and must default to "nil", not "true" or "false". func TestNetworkCreateIPv6(t *testing.T) { - strPtr := func(val bool) *bool { return &val } - tests := []struct { doc, name string flags []string @@ -259,19 +255,19 @@ func TestNetworkCreateIPv6(t *testing.T) { doc: "IPV6 enabled", name: "ipv6-enabled", flags: []string{"--ipv6=true"}, - expected: strPtr(true), + expected: new(true), }, { doc: "IPV6 enabled (shorthand)", name: "ipv6-enabled-shorthand", flags: []string{"--ipv6"}, - expected: strPtr(true), + expected: new(true), }, { doc: "IPV6 disabled", name: "ipv6-disabled", flags: []string{"--ipv6=false"}, - expected: strPtr(false), + expected: new(false), }, } diff --git a/cli/command/stack/loader.go b/cli/command/stack/loader.go index 7c535662ce..49366e4d50 100644 --- a/cli/command/stack/loader.go +++ b/cli/command/stack/loader.go @@ -29,8 +29,7 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes config, err := loader.Load(configDetails) if err != nil { - var fpe *loader.ForbiddenPropertiesError - if errors.As(err, &fpe) { + if fpe, ok := errors.AsType[*loader.ForbiddenPropertiesError](err); ok { // this error is intentionally formatted multi-line return nil, fmt.Errorf("compose file contains unsupported options:\n\n%s\n", propertyWarnings(fpe.Properties)) //nolint:staticcheck // ignore ST1005 } diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index e23d782c7e..762bf144ff 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -399,7 +399,7 @@ func convertFileObject( } mode := config.Mode if mode == nil { - mode = uint32Ptr(0o444) + mode = new(uint32(0o444)) } return swarmReferenceObject{ @@ -413,10 +413,6 @@ func convertFileObject( }, nil } -func uint32Ptr(value uint32) *uint32 { - return &value -} - // convertExtraHosts converts : mappings to SwarmKit notation: // "IP-address hostname(s)". The original order of mappings is preserved. func convertExtraHosts(extraHosts composetypes.HostsList) []string { @@ -501,8 +497,7 @@ func convertRestartPolicy(restart string, restartPolicy *composetypes.RestartPol if i <= 0 { return nil } - p := uint64(i) - return &p + return new(uint64(i)) } switch policy.Name { @@ -529,7 +524,7 @@ func convertUpdateConfig(source *composetypes.UpdateConfig) *swarm.UpdateConfig if source == nil { return nil } - parallel := uint64(1) + var parallel uint64 = 1 if source.Parallelism != nil { parallel = *source.Parallelism } diff --git a/cli/compose/convert/service_test.go b/cli/compose/convert/service_test.go index 2ef349e731..7cd4db46e6 100644 --- a/cli/compose/convert/service_test.go +++ b/cli/compose/convert/service_test.go @@ -66,14 +66,10 @@ func TestConvertRestartPolicy(t *testing.T) { } } -func strPtr(val string) *string { - return &val -} - func TestConvertEnvironment(t *testing.T) { source := map[string]*string{ - "foo": strPtr("bar"), - "key": strPtr("value"), + "foo": new("bar"), + "key": new("value"), } env := convertEnvironment(source) assert.Check(t, is.DeepEqual([]string{"foo=bar", "key=value"}, env)) @@ -81,7 +77,7 @@ func TestConvertEnvironment(t *testing.T) { func TestConvertEnvironmentWhenNilValueExists(t *testing.T) { source := map[string]*string{ - "key": strPtr("value"), + "key": new("value"), "keyWithNoValue": nil, } env := convertEnvironment(source) @@ -466,7 +462,7 @@ func TestConvertFileObject(t *testing.T) { Target: "target", UID: "user", GID: "group", - Mode: uint32Ptr(0o644), + Mode: new(uint32(0o644)), } swarmRef, err := convertFileObject(namespace, config, lookupConfig) assert.NilError(t, err) diff --git a/cli/compose/loader/full-struct_test.go b/cli/compose/loader/full-struct_test.go index 04176bbf5a..9a6e7a76d3 100644 --- a/cli/compose/loader/full-struct_test.go +++ b/cli/compose/loader/full-struct_test.go @@ -36,7 +36,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig { Build: types.BuildConfig{ Context: "./dir", Dockerfile: "Dockerfile", - Args: map[string]*string{"foo": strPtr("bar")}, + Args: map[string]*string{"foo": new("bar")}, Target: "foo", Network: "foo", CacheFrom: []string{"foo", "bar"}, @@ -59,17 +59,17 @@ func services(workingDir, homeDir string) []types.ServiceConfig { Target: "/my_config", UID: "103", GID: "103", - Mode: uint32Ptr(0o440), + Mode: new(uint32(0o440)), }, }, ContainerName: "my-web-container", DependsOn: []string{"db", "redis"}, Deploy: types.DeployConfig{ Mode: "replicated", - Replicas: uint64Ptr(6), + Replicas: new(uint64(6)), Labels: map[string]string{"FOO": "BAR"}, RollbackConfig: &types.UpdateConfig{ - Parallelism: uint64Ptr(3), + Parallelism: new(uint64(3)), Delay: types.Duration(10 * time.Second), FailureAction: "continue", Monitor: types.Duration(60 * time.Second), @@ -77,7 +77,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig { Order: "start-first", }, UpdateConfig: &types.UpdateConfig{ - Parallelism: uint64Ptr(3), + Parallelism: new(uint64(3)), Delay: types.Duration(10 * time.Second), FailureAction: "continue", Monitor: types.Duration(60 * time.Second), @@ -111,9 +111,9 @@ func services(workingDir, homeDir string) []types.ServiceConfig { }, RestartPolicy: &types.RestartPolicy{ Condition: "on-failure", - Delay: durationPtr(5 * time.Second), - MaxAttempts: uint64Ptr(3), - Window: durationPtr(2 * time.Minute), + Delay: new(types.Duration(5 * time.Second)), + MaxAttempts: new(uint64(3)), + Window: new(types.Duration(2 * time.Minute)), }, Placement: types.Placement{ Constraints: []string{"node=foo"}, @@ -132,10 +132,10 @@ func services(workingDir, homeDir string) []types.ServiceConfig { DomainName: "foo.com", Entrypoint: []string{"/code/entrypoint.sh", "-p", "3000"}, Environment: map[string]*string{ - "FOO": strPtr("foo_from_env_file"), - "BAR": strPtr("bar_from_env_file_2"), - "BAZ": strPtr("baz_from_service_def"), - "QUX": strPtr("qux_from_environment"), + "FOO": new("foo_from_env_file"), + "BAR": new("bar_from_env_file_2"), + "BAZ": new("baz_from_service_def"), + "QUX": new("qux_from_environment"), }, EnvFile: []string{ "./example1.env", @@ -158,11 +158,11 @@ func services(workingDir, homeDir string) []types.ServiceConfig { }, HealthCheck: &types.HealthCheckConfig{ Test: types.HealthCheckTest([]string{"CMD-SHELL", "echo \"hello world\""}), - Interval: durationPtr(10 * time.Second), - Timeout: durationPtr(1 * time.Second), - Retries: uint64Ptr(5), - StartPeriod: durationPtr(15 * time.Second), - StartInterval: durationPtr(1 * time.Second), + Interval: new(types.Duration(10 * time.Second)), + Timeout: new(types.Duration(1 * time.Second)), + Retries: new(uint64(5)), + StartPeriod: new(types.Duration(15 * time.Second)), + StartInterval: new(types.Duration(1 * time.Second)), }, Hostname: "foo", Image: "redis", @@ -348,7 +348,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig { Target: "my_secret", UID: "103", GID: "103", - Mode: uint32Ptr(0o440), + Mode: new(uint32(0o440)), }, }, SecurityOpt: []string{ @@ -357,7 +357,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig { }, StdinOpen: true, StopSignal: "SIGUSR1", - StopGracePeriod: durationPtr(20 * time.Second), + StopGracePeriod: new(types.Duration(20 * time.Second)), Sysctls: map[string]string{ "net.core.somaxconn": "1024", "net.ipv4.tcp_syncookies": "0", diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index 294cec6567..1b97ffaf2d 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -9,7 +9,6 @@ import ( "runtime" "sort" "testing" - "time" "github.com/docker/cli/cli/compose/types" "github.com/google/go-cmp/cmp/cmpopts" @@ -179,10 +178,6 @@ var samplePortsConfig = []types.ServicePortConfig{ }, } -func strPtr(val string) *string { - return &val -} - var sampleConfig = types.Config{ Version: "3.13", Services: []types.ServiceConfig{ @@ -197,7 +192,7 @@ var sampleConfig = types.Config{ { Name: "bar", Image: "busybox", - Environment: map[string]*string{"FOO": strPtr("1")}, + Environment: map[string]*string{"FOO": new("1")}, Networks: map[string]*types.ServiceNetworkConfig{ "with_ipam": nil, }, @@ -530,10 +525,10 @@ services: assert.NilError(t, err) expected := types.MappingWithEquals{ - "FOO": strPtr("1"), - "BAR": strPtr("2"), - "BAZ": strPtr("2.5"), - "QUX": strPtr("qux"), + "FOO": new("1"), + "BAR": new("2"), + "BAZ": new("2.5"), + "QUX": new("qux"), "QUUX": nil, } @@ -685,31 +680,31 @@ networks: Configs: []types.ServiceConfigObjConfig{ { Source: "appconfig", - Mode: uint32Ptr(555), + Mode: new(uint32(555)), }, }, Secrets: []types.ServiceSecretConfig{ { Source: "super", - Mode: uint32Ptr(555), + Mode: new(uint32(555)), }, }, HealthCheck: &types.HealthCheckConfig{ - Retries: uint64Ptr(555), + Retries: new(uint64(555)), Disable: true, }, Deploy: types.DeployConfig{ - Replicas: uint64Ptr(555), + Replicas: new(uint64(555)), UpdateConfig: &types.UpdateConfig{ - Parallelism: uint64Ptr(555), + Parallelism: new(uint64(555)), MaxFailureRatio: 3.14, }, RollbackConfig: &types.UpdateConfig{ - Parallelism: uint64Ptr(555), + Parallelism: new(uint64(555)), MaxFailureRatio: 3.14, }, RestartPolicy: &types.RestartPolicy{ - MaxAttempts: uint64Ptr(555), + MaxAttempts: new(uint64(555)), }, Placement: types.Placement{ MaxReplicas: 555, @@ -798,10 +793,10 @@ services: - example2.env `)) expectedEnvironmentMap := types.MappingWithEquals{ - "FOO": strPtr("foo_from_env_file"), - "BAZ": strPtr("baz_from_env_file"), - "BAR": strPtr("bar_from_env_file_2"), // Original value is overwritten by example2.env - "QUX": strPtr("quz_from_env_file_2"), + "FOO": new("foo_from_env_file"), + "BAZ": new("baz_from_env_file"), + "BAR": new("bar_from_env_file_2"), // Original value is overwritten by example2.env + "QUX": new("quz_from_env_file_2"), } assert.NilError(t, err) configDetails := buildConfigDetails(dict, nil) @@ -957,19 +952,6 @@ volumes: assert.Check(t, is.ErrorContains(err, `external_volume`)) } -func durationPtr(value time.Duration) *types.Duration { - result := types.Duration(value) - return &result -} - -func uint64Ptr(value uint64) *uint64 { - return &value -} - -func uint32Ptr(value uint32) *uint32 { - return &value -} - func TestFullExample(t *testing.T) { skip.If(t, runtime.GOOS == "windows", "FIXME: substitutes platform-specific HOME-dirs and requires platform-specific golden files; see https://github.com/docker/cli/pull/4610") diff --git a/cli/compose/loader/merge_test.go b/cli/compose/loader/merge_test.go index 2f6b456174..cd09a2beb5 100644 --- a/cli/compose/loader/merge_test.go +++ b/cli/compose/loader/merge_test.go @@ -920,8 +920,8 @@ func TestLoadMultipleConfigs(t *testing.T) { Context: ".", Dockerfile: "foo.Dockerfile", Args: types.MappingWithEquals{ - "buildno": strPtr("1"), - "password": strPtr("secret"), + "buildno": new("1"), + "password": new("secret"), }, }, Ports: []types.ServicePortConfig{ @@ -1246,13 +1246,13 @@ func TestMergeServiceOverrideReplicasZero(t *testing.T) { base := types.ServiceConfig{ Name: "someService", Deploy: types.DeployConfig{ - Replicas: uint64Ptr(3), + Replicas: new(uint64(3)), }, } override := types.ServiceConfig{ Name: "someService", Deploy: types.DeployConfig{ - Replicas: uint64Ptr(0), + Replicas: new(uint64(0)), }, } services, err := mergeServices([]types.ServiceConfig{base}, []types.ServiceConfig{override}) @@ -1265,7 +1265,7 @@ func TestMergeServiceOverrideReplicasZero(t *testing.T) { types.ServiceConfig{ Name: "someService", Deploy: types.DeployConfig{ - Replicas: uint64Ptr(0), + Replicas: new(uint64(0)), }, }, ) @@ -1275,7 +1275,7 @@ func TestMergeServiceOverrideReplicasNotNil(t *testing.T) { base := types.ServiceConfig{ Name: "someService", Deploy: types.DeployConfig{ - Replicas: uint64Ptr(3), + Replicas: new(uint64(3)), }, } override := types.ServiceConfig{ @@ -1292,7 +1292,7 @@ func TestMergeServiceOverrideReplicasNotNil(t *testing.T) { types.ServiceConfig{ Name: "someService", Deploy: types.DeployConfig{ - Replicas: uint64Ptr(3), + Replicas: new(uint64(3)), }, }, ) diff --git a/cli/compose/schema/schema.go b/cli/compose/schema/schema.go index 9ea79644e6..f42130a0de 100644 --- a/cli/compose/schema/schema.go +++ b/cli/compose/schema/schema.go @@ -101,8 +101,7 @@ func Validate(config map[string]any, version string) error { } if err := schema.Validate(toJSONValue(config)); err != nil { - var validationErr *jsonschema.ValidationError - if errors.As(err, &validationErr) { + if validationErr, ok := errors.AsType[*jsonschema.ValidationError](err); ok { return getMostSpecificError(validationErr) } return err diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 73f46aae8e..2339f60ae3 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -79,8 +79,7 @@ func ConvertDurationPtr(d *Duration) *time.Duration { if d == nil { return nil } - res := time.Duration(*d) - return &res + return new(time.Duration(*d)) } // MarshalJSON makes Duration implement json.Marshaler diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index 5edce29158..e32b1e767c 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -232,8 +232,7 @@ func (c *ConfigFile) Save() (retErr error) { cfgFile = f } else if os.IsNotExist(err) { // extract the path from the error if the configfile does not exist or is a dangling symlink - var pathError *os.PathError - if errors.As(err, &pathError) { + if pathError, ok := errors.AsType[*os.PathError](err); ok { cfgFile = pathError.Path } } diff --git a/cli/connhelper/ssh/ssh.go b/cli/connhelper/ssh/ssh.go index 2fcb54a98f..b91f2bc02e 100644 --- a/cli/connhelper/ssh/ssh.go +++ b/cli/connhelper/ssh/ssh.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + // Package ssh provides the connection helper for ssh:// URL. package ssh @@ -15,8 +18,7 @@ import ( func ParseURL(daemonURL string) (*Spec, error) { u, err := url.Parse(daemonURL) if err != nil { - var urlErr *url.Error - if errors.As(err, &urlErr) { + if urlErr, ok := errors.AsType[*url.Error](err); ok { err = urlErr.Unwrap() } return nil, fmt.Errorf("invalid SSH URL: %w", err) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index aca19c76cf..7987ffa3f7 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -101,8 +101,7 @@ func getExitCode(err error) int { return 0 } - var userTerminatedErr errCtxSignalTerminated - if errors.As(err, &userTerminatedErr) { + if userTerminatedErr, ok := errors.AsType[errCtxSignalTerminated](err); ok { s, ok := userTerminatedErr.signal.(syscall.Signal) if !ok { return 1 diff --git a/internal/registry/errors.go b/internal/registry/errors.go index 20a5fca373..7b12a8461f 100644 --- a/internal/registry/errors.go +++ b/internal/registry/errors.go @@ -12,8 +12,7 @@ import ( ) func translateV2AuthError(err error) error { - var e *url.Error - if errors.As(err, &e) { + if e, ok := errors.AsType[*url.Error](err); ok { var e2 errcode.Error if errors.As(e, &e2) && errors.Is(e2.Code, errcode.ErrorCodeUnauthorized) { return unauthorizedErr{err} diff --git a/internal/registryclient/fetcher.go b/internal/registryclient/fetcher.go index cad5451f8f..5bc308b4cb 100644 --- a/internal/registryclient/fetcher.go +++ b/internal/registryclient/fetcher.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package registryclient import ( @@ -244,8 +247,7 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named, repo, err := c.getRepositoryForReference(ctx, namedRef, repoEndpoint) if err != nil { logrus.Debugf("error %s with repo endpoint %+v", err, repoEndpoint) - var protoErr httpProtoError - if errors.As(err, &protoErr) { + if _, ok := errors.AsType[httpProtoError](err); ok { continue } return err diff --git a/opts/gpus.go b/opts/gpus.go index e68ede7a08..3905a691ac 100644 --- a/opts/gpus.go +++ b/opts/gpus.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package opts import ( @@ -21,8 +24,7 @@ func parseCount(s string) (int, error) { } i, err := strconv.Atoi(s) if err != nil { - var numErr *strconv.NumError - if errors.As(err, &numErr) { + if numErr, ok := errors.AsType[*strconv.NumError](err); ok { err = numErr.Err } return 0, fmt.Errorf(`invalid count (%s): value must be either "all" or an integer: %w`, s, err) diff --git a/opts/network.go b/opts/network.go index 489ef8be39..51f50f2070 100644 --- a/opts/network.go +++ b/opts/network.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package opts import ( @@ -100,8 +103,7 @@ func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo case gwPriorityOpt: netOpt.GwPriority, err = strconv.Atoi(val) if err != nil { - var numErr *strconv.NumError - if errors.As(err, &numErr) { + if numErr, ok := errors.AsType[*strconv.NumError](err); ok { err = numErr.Err } return fmt.Errorf("invalid gw-priority (%s): %w", val, err) diff --git a/opts/swarmopts/port.go b/opts/swarmopts/port.go index f3caf32f46..589c127959 100644 --- a/opts/swarmopts/port.go +++ b/opts/swarmopts/port.go @@ -73,8 +73,7 @@ func (p *PortOpt) Set(value string) error { case portOptTargetPort: tPort, err := strconv.ParseUint(val, 10, 16) if err != nil { - var numErr *strconv.NumError - if errors.As(err, &numErr) { + if numErr, ok := errors.AsType[*strconv.NumError](err); ok { err = numErr.Err } return fmt.Errorf("invalid target port (%s): value must be an integer: %w", val, err) @@ -84,8 +83,7 @@ func (p *PortOpt) Set(value string) error { case portOptPublishedPort: pPort, err := strconv.ParseUint(val, 10, 16) if err != nil { - var numErr *strconv.NumError - if errors.As(err, &numErr) { + if numErr, ok := errors.AsType[*strconv.NumError](err); ok { err = numErr.Err } return fmt.Errorf("invalid published port (%s): value must be an integer: %w", val, err) From 4239f5d1214bae4b2174d0df8e7b760ec9ba5dad Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 28 Aug 2026 21:11:47 +0200 Subject: [PATCH 11/44] go fix (omitzero) Signed-off-by: Sebastiaan van Stijn --- cli/command/system/version.go | 2 +- cli/compose/types/types.go | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/command/system/version.go b/cli/command/system/version.go index 698fb70917..b385ef7181 100644 --- a/cli/command/system/version.go +++ b/cli/command/system/version.go @@ -87,7 +87,7 @@ type clientVersion struct { // serverVersion contains information about the Docker server host. // it's the client-side presentation of [client.ServerVersionResult]. type serverVersion struct { - Platform client.PlatformInfo `json:",omitempty"` // Platform is the platform (product name) the server is running on. + Platform client.PlatformInfo `json:"Platform"` // Platform is the platform (product name) the server is running on. Version string `json:"Version"` // Version is the version of the daemon. APIVersion string `json:"ApiVersion"` // APIVersion is the highest API version supported by the server. MinAPIVersion string `json:"MinAPIVersion,omitempty"` // MinAPIVersion is the minimum API version the server supports. diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 2339f60ae3..ad59ce052b 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -158,7 +158,7 @@ func (s Services) MarshalJSON() ([]byte, error) { type ServiceConfig struct { Name string `yaml:"-" json:"-"` - Build BuildConfig `yaml:",omitempty" json:"build,omitempty"` + Build BuildConfig `yaml:",omitempty" json:"build"` CapAdd []string `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"` CapDrop []string `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` CgroupNSMode string `mapstructure:"cgroupns_mode" yaml:"cgroupns_mode,omitempty" json:"cgroupns_mode,omitempty"` @@ -166,9 +166,9 @@ type ServiceConfig struct { Command ShellCommand `yaml:",omitempty" json:"command,omitempty"` Configs []ServiceConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"` ContainerName string `mapstructure:"container_name" yaml:"container_name,omitempty" json:"container_name,omitempty"` - CredentialSpec CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"` + CredentialSpec CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec"` DependsOn []string `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"` - Deploy DeployConfig `yaml:",omitempty" json:"deploy,omitempty"` + Deploy DeployConfig `yaml:",omitempty" json:"deploy"` Devices []string `yaml:",omitempty" json:"devices,omitempty"` DNS StringList `yaml:",omitempty" json:"dns,omitempty"` DNSSearch StringList `mapstructure:"dns_search" yaml:"dns_search,omitempty" json:"dns_search,omitempty"` @@ -273,9 +273,9 @@ type DeployConfig struct { Labels Labels `yaml:",omitempty" json:"labels,omitempty"` UpdateConfig *UpdateConfig `mapstructure:"update_config" yaml:"update_config,omitempty" json:"update_config,omitempty"` RollbackConfig *UpdateConfig `mapstructure:"rollback_config" yaml:"rollback_config,omitempty" json:"rollback_config,omitempty"` - Resources Resources `yaml:",omitempty" json:"resources,omitempty"` + Resources Resources `yaml:",omitempty" json:"resources"` RestartPolicy *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty" json:"restart_policy,omitempty"` - Placement Placement `yaml:",omitempty" json:"placement,omitempty"` + Placement Placement `yaml:",omitempty" json:"placement"` EndpointMode string `mapstructure:"endpoint_mode" yaml:"endpoint_mode,omitempty" json:"endpoint_mode,omitempty"` } @@ -453,8 +453,8 @@ type NetworkConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` - Ipam IPAMConfig `yaml:",omitempty" json:"ipam,omitempty"` - External External `yaml:",omitempty" json:"external,omitempty"` + Ipam IPAMConfig `yaml:",omitempty" json:"ipam"` + External External `yaml:",omitempty" json:"external"` Internal bool `yaml:",omitempty" json:"internal,omitempty"` Attachable bool `yaml:",omitempty" json:"attachable,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` @@ -477,7 +477,7 @@ type VolumeConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` - External External `yaml:",omitempty" json:"external,omitempty"` + External External `yaml:",omitempty" json:"external"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Extras map[string]any `yaml:",inline" json:"-"` Spec *ClusterVolumeSpec `mapstructure:"x-cluster-spec" yaml:"x-cluster-spec,omitempty" json:"x-cluster-spec,omitempty"` @@ -574,7 +574,7 @@ type CredentialSpecConfig struct { type FileObjectConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` File string `yaml:",omitempty" json:"file,omitempty"` - External External `yaml:",omitempty" json:"external,omitempty"` + External External `yaml:",omitempty" json:"external"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Extras map[string]any `yaml:",inline" json:"-"` Driver string `yaml:",omitempty" json:"driver,omitempty"` From 63da0b015bdf76f1a107f24770732c9a64804454 Mon Sep 17 00:00:00 2001 From: FrancescoCastaldi Date: Thu, 27 Aug 2026 16:38:20 +0200 Subject: [PATCH 12/44] completion: fix zsh __docker_plugins bad math expression Use 'docker plugin ls --format' to extract plugin names and IDs, escaping colons in the plugin name (e.g. repo:tag) so zsh _describe treats the ID correctly as description. Fixes #2761 Signed-off-by: FrancescoCastaldi --- contrib/completion/zsh/_docker | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 486e5a5d17..1efb30f86a 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -1551,32 +1551,18 @@ __docker_plugin_complete_ls_filters() { __docker_plugins() { [[ $PREFIX = -* ]] && return 1 integer ret=1 - local line s + local line name id declare -a lines plugins args filter=$1; shift - [[ $filter != "none" ]] && args=("-f $filter") + [[ $filter != "none" ]] && args=("-f" "$filter") - lines=(${(f)${:-"$(_call_program commands docker $docker_options plugin ls $args)"$'\n'}}) + lines=(${(f)${:-"$(_call_program commands docker $docker_options plugin ls --format '{{.Name}}\t{{.ID}}' $args)"$'\n'}}) - # Parse header line to find columns - local i=1 j=1 k header=${lines[1]} - declare -A begin end - while (( j < ${#header} - 1 )); do - i=$(( j + ${${header[$j,-1]}[(i)[^ ]]} - 1 )) - j=$(( i + ${${header[$i,-1]}[(i) ]} - 1 )) - k=$(( j + ${${header[$j,-1]}[(i)[^ ]]} - 2 )) - begin[${header[$i,$((j-1))]}]=$i - end[${header[$i,$((j-1))]}]=$k - done - end[${header[$i,$((j-1))]}]=-1 - lines=(${lines[2,-1]}) - - # Name for line in $lines; do - s="${line[${begin[NAME]},${end[NAME]}]%% ##}" - s="$s:${(l:7:: :::)${${line[${begin[TAG]},${end[TAG]}]}%% ##}}" - plugins=($plugins $s) + name="${line%%$'\t'*}" + id="${line#*$'\t'}" + plugins+=("${name//:/\\:}:$id") done _describe -t plugins-list "plugins" plugins "$@" && ret=0 From cffd24dea48ccb1347520ce917e083a72c085667 Mon Sep 17 00:00:00 2001 From: Mohammed Olabie Date: Mon, 11 May 2026 18:33:45 +0300 Subject: [PATCH 13/44] cmd/docker: print command error before running plugin hooks Plugin hook output (such as Gordon's "What's next:" hint) was rendered before the command's own error message because hooks were invoked inside runDocker while the error was only printed in main() after runDocker returned. Print the error to stderr before invoking the hooks, and replace the error with a status-only StatusError so main() does not print the same message a second time. The original error message is captured up-front and still passed to the plugin hooks. The inline error-printing block in runDocker is extracted into a printCommandError helper so the new behavior can be unit-tested directly without spinning up runDocker. TestPrintCommandError covers each branch of the helper: nil error, generic error (printed and replaced with StatusError), StatusError with/without message, context.Canceled (raw and wrapped), and errCtxSignalTerminated. Signed-off-by: Mohammed Olabie Signed-off-by: Sebastiaan van Stijn --- cmd/docker/docker.go | 37 ++++++++++++++++++- cmd/docker/docker_test.go | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 7987ffa3f7..2341570b91 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "io" "os" "os/exec" "os/signal" @@ -470,6 +471,34 @@ func restoreTerminal(streams command.Streams) { streams.Err().RestoreTerminal() } +// printCommandError prints err to stderr before plugin hooks run, so that +// hook output (such as the "What's next:" hint) is rendered after the +// command's own error output instead of before it. +// +// Errors caused by context cancellation, user-initiated signal termination, +// or errors with an empty message are not printed (matching the conditions +// used in [main]). +// +// If err was printed, it is replaced with a status-only [cli.StatusError] +// preserving the exit code, so that [main] does not print the same message +// a second time. +func printCommandError(stderr io.Writer, err error) error { + if err == nil { + return nil + } + if errdefs.IsCanceled(err) { + return err + } + if errors.As(err, &errCtxSignalTerminated{}) { + return err + } + if err.Error() == "" { + return err + } + _, _ = fmt.Fprintln(stderr, err) + return cli.StatusError{StatusCode: getExitCode(err)} +} + //nolint:gocyclo func runDocker(ctx context.Context, dockerCli *command.DockerCli) error { tcmd := newDockerCommand(dockerCli) @@ -519,6 +548,7 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error { err := tryPluginRun(ctx, dockerCli, cmd, args[0], envs) if ccmd != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() && !errdefs.IsNotFound(err) { errMessage := cmdErrorMessage(err) + err = printCommandError(dockerCli.Err(), err) pluginmanager.RunPluginHooks(ctx, dockerCli, cmd, ccmd, args, errMessage) } if err == nil { @@ -543,9 +573,12 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error { err = cmd.ExecuteContext(ctx) // If the command is being executed in an interactive terminal - // and hook are enabled, run the plugin hooks. + // and hooks are enabled, run the plugin hooks. Print the command's + // error first so that hook output is rendered after the error. if subCommand != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() { - pluginmanager.RunCLICommandHooks(ctx, dockerCli, cmd, subCommand, cmdErrorMessage(err)) + errMessage := cmdErrorMessage(err) + err = printCommandError(dockerCli.Err(), err) + pluginmanager.RunCLICommandHooks(ctx, dockerCli, cmd, subCommand, errMessage) } return err diff --git a/cmd/docker/docker_test.go b/cmd/docker/docker_test.go index 928fcc6ee2..e68ba48db9 100644 --- a/cmd/docker/docker_test.go +++ b/cmd/docker/docker_test.go @@ -163,6 +163,84 @@ func TestGetExitCode(t *testing.T) { }) } +func TestPrintCommandError(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + var buf bytes.Buffer + got := printCommandError(&buf, nil) + + assert.NilError(t, got) + assert.Equal(t, buf.String(), "") + }) + + t.Run("generic error", func(t *testing.T) { + var buf bytes.Buffer + orig := errors.New("docker: open ./no-such-file: no such file or directory") + got := printCommandError(&buf, orig) + + assert.Equal(t, buf.String(), orig.Error()+"\n") + + var st dockercli.StatusError + assert.Assert(t, errors.As(got, &st)) + assert.Equal(t, st.Status, "") + assert.Equal(t, st.StatusCode, 1) + }) + + t.Run("StatusError with message", func(t *testing.T) { + var buf bytes.Buffer + got := printCommandError(&buf, dockercli.StatusError{ + Status: "build failed", + StatusCode: 125, + }) + + assert.Equal(t, buf.String(), "build failed\n") + + var st dockercli.StatusError + assert.Assert(t, errors.As(got, &st)) + assert.Equal(t, st.Status, "") + assert.Equal(t, st.StatusCode, 125) + }) + + t.Run("StatusError without message", func(t *testing.T) { + var buf bytes.Buffer + got := printCommandError(&buf, dockercli.StatusError{StatusCode: 42}) + + assert.Equal(t, buf.String(), "") + + var st dockercli.StatusError + assert.Assert(t, errors.As(got, &st)) + assert.Equal(t, st.Status, "") + assert.Equal(t, st.StatusCode, 42) + }) + + t.Run("canceled error", func(t *testing.T) { + var buf bytes.Buffer + got := printCommandError(&buf, context.Canceled) + + assert.Equal(t, buf.String(), "") + assert.ErrorIs(t, got, context.Canceled) + }) + + t.Run("wrapped canceled error", func(t *testing.T) { + var buf bytes.Buffer + got := printCommandError(&buf, fmt.Errorf("wrapped: %w", context.Canceled)) + + assert.Equal(t, buf.String(), "") + assert.ErrorIs(t, got, context.Canceled) + }) + + t.Run("signal-terminated error", func(t *testing.T) { + var buf bytes.Buffer + orig := errCtxSignalTerminated{signal: syscall.SIGINT} + got := printCommandError(&buf, orig) + + assert.Equal(t, buf.String(), "") + + var sig errCtxSignalTerminated + assert.Assert(t, errors.As(got, &sig)) + assert.Equal(t, sig, orig) + }) +} + func TestCmdErrorMessage(t *testing.T) { t.Run("nil error returns empty string", func(t *testing.T) { assert.Equal(t, cmdErrorMessage(nil), "") From 1164af3e600c74f576d7350b1289a65229c32287 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 08:41:28 +0000 Subject: [PATCH 14/44] build(deps): bump docker/docker-agent-action/.github/workflows/review-pr.yml Bumps the docker-actions group with 1 update in the / directory: [docker/docker-agent-action/.github/workflows/review-pr.yml](https://github.com/docker/docker-agent-action). Updates `docker/docker-agent-action/.github/workflows/review-pr.yml` from 2.0.4 to 2.0.5 - [Release notes](https://github.com/docker/docker-agent-action/releases) - [Commits](https://github.com/docker/docker-agent-action/compare/b08367e7e9a471822464be2bb9fdaa1c0fb6061b...06e1767af06263c93d712449cbf859778d9392ee) --- updated-dependencies: - dependency-name: docker/docker-agent-action/.github/workflows/review-pr.yml dependency-version: 2.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docker-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/pr-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index d1f47785aa..2e87b3220f 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -11,7 +11,7 @@ permissions: jobs: review: - uses: docker/docker-agent-action/.github/workflows/review-pr.yml@b08367e7e9a471822464be2bb9fdaa1c0fb6061b # v2.0.4 + uses: docker/docker-agent-action/.github/workflows/review-pr.yml@06e1767af06263c93d712449cbf859778d9392ee # v2.0.5 permissions: contents: read # Read repository files and PR diffs pull-requests: write # Post review comments From a458c3871c2d0ce05824ecb140bb62aec2faa3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Tue, 1 Sep 2026 16:19:27 +0200 Subject: [PATCH 15/44] dockerd: Document `default-stop-timeout` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- docs/reference/dockerd.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/reference/dockerd.md b/docs/reference/dockerd.md index 1dcb191626..71cb56212b 100644 --- a/docs/reference/dockerd.md +++ b/docs/reference/dockerd.md @@ -51,6 +51,7 @@ Options: --default-network-opt mapmap Default network options (default map[]) --default-runtime string Default OCI runtime for containers (default "runc") --default-shm-size bytes Default shm size for containers (default 64MiB) + --default-stop-timeout int Set the default container stop timeout in seconds (0 to terminate immediately) (default 10) --default-ulimit ulimit Default ulimits for containers (default []) --dns list DNS server to use --dns-opt list DNS options to use @@ -758,6 +759,18 @@ This only adds the proxy and authentication to the Docker daemon's requests. To use the proxy when building images and running containers, see [Configure Docker to use a proxy server](https://docs.docker.com/engine/cli/proxy/) +### Default container stop timeout + +The `--default-stop-timeout` flag sets the number of seconds to wait for a +container to stop when the container doesn't have a `--stop-timeout` configured. +The default is `10` seconds on Linux and `30` seconds on Windows. A value of `0` +doesn't wait before forcefully terminating the container. Negative values are +invalid. + +You can also configure the timeout with the `default-stop-timeout` option in the +[daemon configuration file](#daemon-configuration-file). You can reload this +option without restarting the daemon. + ### Default `ulimit` settings The `--default-ulimit` flag lets you set the default `ulimit` options to use for @@ -1104,6 +1117,7 @@ The following is a full example of the allowed configuration options on Linux: "default-network-opts": {}, "default-runtime": "runc", "default-shm-size": "64M", + "default-stop-timeout": 10, "default-ulimits": { "nofile": { "Hard": 64000, @@ -1225,6 +1239,7 @@ The following is a full example of the allowed configuration options on Windows: "debug": true, "default-network-opts": {}, "default-runtime": "", + "default-stop-timeout": 30, "default-ulimits": {}, "dns": [], "dns-opts": [], @@ -1317,6 +1332,7 @@ The list of currently supported options that can be reconfigured is this: | `max-concurrent-uploads` | Configures the max concurrent uploads for each push. | | `max-download-attempts` | Configures the max download attempts for each pull. | | `default-runtime` | Configures the runtime to be used if not is specified at container creation. | +| `default-stop-timeout` | Configures the timeout for stopping containers that have no container-specific timeout. | | `runtimes` | Configures the list of available OCI runtimes that can be used to run containers. | | `authorization-plugin` | Specifies the authorization plugins to use. | | `insecure-registries` | Specifies a list of registries that the daemon should consider insecure. | From 7b7eb0ef6b0cb935e0e3b59474644c302cb73596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Tue, 1 Sep 2026 16:33:04 +0200 Subject: [PATCH 16/44] dockerd: Document custom AppArmor profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- docs/reference/dockerd.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/reference/dockerd.md b/docs/reference/dockerd.md index 71cb56212b..61a342c655 100644 --- a/docs/reference/dockerd.md +++ b/docs/reference/dockerd.md @@ -25,6 +25,7 @@ A self-sufficient runtime for containers. Options: --add-runtime runtime Register an additional OCI compatible runtime (default []) --allow-direct-routing Allow remote access to published ports on container IP addresses + --apparmor-profile string Path to AppArmor profile template --authorization-plugin list Authorization plugins to load --bip string IPv4 address for the default bridge --bip6 string IPv6 address for the default bridge @@ -806,6 +807,24 @@ allow the request for it to complete. For information about how to create an authorization plugin, refer to the [authorization plugin](https://docs.docker.com/engine/extend/plugins_authorization/) section. +### Configure the default AppArmor profile + +The `--apparmor-profile` option sets the path to an AppArmor profile definition +for Docker's default `docker-default` container profile. This option is only +supported on Linux hosts. The file can be a static profile or a Go template. + +The equivalent `daemon.json` configuration is: + +```json +{ + "apparmor-profile": "/etc/docker/apparmor/docker-default" +} +``` + +Restart the daemon to apply changes. For template values, safe file placement, +security considerations, and verification, see +[Customize the default AppArmor profile](https://docs.docker.com/engine/security/apparmor/#customize-the-default-profile). + ### Daemon user namespace options The Linux kernel @@ -1079,6 +1098,7 @@ The following is a full example of the allowed configuration options on Linux: ```json { "allow-direct-routing": false, + "apparmor-profile": "", "authorization-plugins": [], "bip": "", "bip6": "", From b4189c2f7ff09766fd50a416ec90c9ac7a086c26 Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Sat, 29 Aug 2026 02:40:37 +0500 Subject: [PATCH 17/44] registry: map login HTTP status through errhttp.ToNative translateV2AuthError only unwraps a url.Error from Do(); a 401 status never went through it. Use errhttp.ToNative so Auth() treats that 401 as unauthorized and stops. Signed-off-by: Dean Chen <862469039@qq.com> Signed-off-by: Sebastiaan van Stijn --- internal/registry/auth.go | 7 ++----- internal/registry/auth_test.go | 1 + vendor.mod | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/registry/auth.go b/internal/registry/auth.go index 1c0ccb6759..4844710e66 100644 --- a/internal/registry/auth.go +++ b/internal/registry/auth.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/containerd/errdefs/pkg/errhttp" "github.com/containerd/log" "github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth/challenge" @@ -67,11 +68,7 @@ func loginV2(ctx context.Context, authConfig *registry.AuthConfig, endpoint APIE if resp.StatusCode != http.StatusOK { // TODO(dmcgowan): Attempt to further interpret result, status code and error code string - err := fmt.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) - if resp.StatusCode == http.StatusUnauthorized { - return "", unauthorizedErr{err} - } - return "", err + return "", fmt.Errorf("login attempt to %s failed with status: %d %s: %w", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode), errhttp.ToNative(resp.StatusCode)) } return credentialAuthConfig.IdentityToken, nil diff --git a/internal/registry/auth_test.go b/internal/registry/auth_test.go index 4b7f1a8d5b..44632df42e 100644 --- a/internal/registry/auth_test.go +++ b/internal/registry/auth_test.go @@ -33,6 +33,7 @@ func TestLoginV2BasicAuthUnauthorized(t *testing.T) { _, err = loginV2(ctx, ®istry.AuthConfig{Username: "alice", Password: "wrong"}, endpoint, "docker-test") assert.ErrorContains(t, err, "401") assert.Check(t, errdefs.IsUnauthorized(err)) + assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized)) token, err := loginV2(ctx, ®istry.AuthConfig{Username: "alice", Password: "secret"}, endpoint, "docker-test") assert.NilError(t, err) diff --git a/vendor.mod b/vendor.mod index 0a21b03055..ea519df145 100644 --- a/vendor.mod +++ b/vendor.mod @@ -14,6 +14,7 @@ tool ( require ( dario.cat/mergo v1.0.2 github.com/containerd/errdefs v1.0.0 + github.com/containerd/errdefs/pkg v0.3.0 github.com/containerd/log v0.1.0 github.com/containerd/platforms v1.0.0-rc.5 github.com/creack/pty v1.1.24 @@ -75,7 +76,6 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 // indirect github.com/docker/go-metrics v0.1.0 // indirect From f0f38d5cb979958fadfb17158a24fd4be4c3d23d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 21:09:39 +0200 Subject: [PATCH 18/44] cmd/docker: rewrite TestStringSliceReplaceAt to table test Signed-off-by: Sebastiaan van Stijn --- cmd/docker/aliases_utils_test.go | 88 ++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/cmd/docker/aliases_utils_test.go b/cmd/docker/aliases_utils_test.go index 7557d388bb..3afc964783 100644 --- a/cmd/docker/aliases_utils_test.go +++ b/cmd/docker/aliases_utils_test.go @@ -7,27 +7,71 @@ import ( ) func TestStringSliceReplaceAt(t *testing.T) { - out, ok := stringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, -1) - assert.Assert(t, ok) - assert.DeepEqual(t, []string{"abc", "baz", "bax"}, out) + tests := []struct { + name string + s []string + find []string + replace []string + requireIndex int + expected []string + ok bool + }{ + { + name: "replace", + s: []string{"abc", "foo", "bar", "bax"}, + find: []string{"foo", "bar"}, + replace: []string{"baz"}, + requireIndex: -1, + expected: []string{"abc", "baz", "bax"}, + ok: true, + }, + { + name: "find longer than input", + s: []string{"foo"}, + find: []string{"foo", "bar"}, + replace: []string{"baz"}, + requireIndex: -1, + expected: []string{"foo"}, + }, + { + name: "wrong required index", + s: []string{"abc", "foo", "bar", "bax"}, + find: []string{"foo", "bar"}, + replace: []string{"baz"}, + requireIndex: 0, + expected: []string{"abc", "foo", "bar", "bax"}, + }, + { + name: "required index", + s: []string{"foo", "bar", "bax"}, + find: []string{"foo", "bar"}, + replace: []string{"baz"}, + requireIndex: 0, + expected: []string{"baz", "bax"}, + ok: true, + }, + { + name: "remove", + s: []string{"abc", "foo", "bar", "baz"}, + find: []string{"foo", "bar"}, + requireIndex: -1, + expected: []string{"abc", "baz"}, + ok: true, + }, + { + name: "empty find", + s: []string{"foo"}, + replace: []string{"baz"}, + requireIndex: -1, + expected: []string{"foo"}, + }, + } - out, ok = stringSliceReplaceAt([]string{"foo"}, []string{"foo", "bar"}, []string{"baz"}, -1) - assert.Assert(t, !ok) - assert.DeepEqual(t, []string{"foo"}, out) - - out, ok = stringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0) - assert.Assert(t, !ok) - assert.DeepEqual(t, []string{"abc", "foo", "bar", "bax"}, out) - - out, ok = stringSliceReplaceAt([]string{"foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0) - assert.Assert(t, ok) - assert.DeepEqual(t, []string{"baz", "bax"}, out) - - out, ok = stringSliceReplaceAt([]string{"abc", "foo", "bar", "baz"}, []string{"foo", "bar"}, nil, -1) - assert.Assert(t, ok) - assert.DeepEqual(t, []string{"abc", "baz"}, out) - - out, ok = stringSliceReplaceAt([]string{"foo"}, nil, []string{"baz"}, -1) - assert.Assert(t, !ok) - assert.DeepEqual(t, []string{"foo"}, out) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + out, ok := stringSliceReplaceAt(tc.s, tc.find, tc.replace, tc.requireIndex) + assert.Equal(t, tc.ok, ok) + assert.DeepEqual(t, tc.expected, out) + }) + } } From bcc4be9f8be792332494453042fe3357e93262d9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 21:10:29 +0200 Subject: [PATCH 19/44] cmd/docker: fix stringSliceReplaceAt with overlapping matches Inline the sub-slice lookup into stringSliceReplaceAt and use slices.Equal to compare candidate ranges. When a specific index is required, check that position directly instead of searching the full slice. This also fixes overlapping matches and uses slices.Concat to construct the replacement result. Signed-off-by: Sebastiaan van Stijn --- cmd/docker/aliases_utils.go | 44 ++++++++++++++------------------ cmd/docker/aliases_utils_test.go | 9 +++++++ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/cmd/docker/aliases_utils.go b/cmd/docker/aliases_utils.go index b14fee0a92..fbf1c7e98e 100644 --- a/cmd/docker/aliases_utils.go +++ b/cmd/docker/aliases_utils.go @@ -1,32 +1,26 @@ package main -func stringSliceIndex(s, subs []string) int { - j := 0 - if len(subs) > 0 { - for i, x := range s { - if j < len(subs) && subs[j] == x { - j++ - } else { - j = 0 - } - if len(subs) == j { - return i + 1 - j - } - } - } - return -1 -} +import "slices" -// stringSliceReplaceAt replaces the sub-slice find, with the sub-slice replace, in the string -// slice s, returning a new slice and a boolean indicating if the replacement happened. -// requireIdx is the index at which old needs to be found at (or -1 to disregard that). +// stringSliceReplaceAt replaces the sub-slice find with the sub-slice replace in s, +// returning a new slice and a boolean indicating whether the replacement happened. +// requireIndex is the index at which find must be found, or -1 to disregard it. func stringSliceReplaceAt(s, find, replace []string, requireIndex int) ([]string, bool) { - idx := stringSliceIndex(s, find) - if (requireIndex != -1 && requireIndex != idx) || idx == -1 { + if len(find) == 0 { return s, false } - out := append([]string{}, s[:idx]...) - out = append(out, replace...) - out = append(out, s[idx+len(find):]...) - return out, true + + if requireIndex >= 0 { + if requireIndex+len(find) > len(s) || !slices.Equal(s[requireIndex:requireIndex+len(find)], find) { + return s, false + } + return slices.Concat(s[:requireIndex], replace, s[requireIndex+len(find):]), true + } + + for i := range len(s) - len(find) + 1 { + if slices.Equal(s[i:i+len(find)], find) { + return slices.Concat(s[:i], replace, s[i+len(find):]), true + } + } + return s, false } diff --git a/cmd/docker/aliases_utils_test.go b/cmd/docker/aliases_utils_test.go index 3afc964783..38db6caf94 100644 --- a/cmd/docker/aliases_utils_test.go +++ b/cmd/docker/aliases_utils_test.go @@ -65,6 +65,15 @@ func TestStringSliceReplaceAt(t *testing.T) { requireIndex: -1, expected: []string{"foo"}, }, + { + name: "overlapping match", + s: []string{"a", "a", "b"}, + find: []string{"a", "b"}, + replace: []string{"c"}, + requireIndex: -1, + expected: []string{"a", "c"}, + ok: true, + }, } for _, tc := range tests { From f838c7c84b768a129cc3c7b7c28a054b74865e6e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 13:27:45 +0200 Subject: [PATCH 20/44] cmd/docker-trust: modernize with slices and maps packages Signed-off-by: Sebastiaan van Stijn --- cmd/docker-trust/internal/trust/trust_push.go | 5 +-- cmd/docker-trust/trust/common.go | 5 +-- cmd/docker-trust/trust/formatter.go | 15 +++---- cmd/docker-trust/trust/inspect.go | 41 ++++++++++--------- cmd/docker-trust/trust/inspect_pretty.go | 7 +++- cmd/docker-trust/trust/sign.go | 9 ++-- 6 files changed, 42 insertions(+), 40 deletions(-) diff --git a/cmd/docker-trust/internal/trust/trust_push.go b/cmd/docker-trust/internal/trust/trust_push.go index 64f255964c..c65085f2c8 100644 --- a/cmd/docker-trust/internal/trust/trust_push.go +++ b/cmd/docker-trust/internal/trust/trust_push.go @@ -7,7 +7,7 @@ import ( "errors" "fmt" "io" - "sort" + "slices" "github.com/distribution/reference" "github.com/docker/cli/cli/streams" @@ -113,8 +113,7 @@ func PushTrustedReference(ctx context.Context, ioStreams Streams, repoInfo *Repo var rootKeyID string // always select the first root key if len(keys) > 0 { - sort.Strings(keys) - rootKeyID = keys[0] + rootKeyID = slices.Min(keys) } else { rootPublicKey, err := repo.GetCryptoService().Create(data.CanonicalRootRole, "", data.ECDSAKey) if err != nil { diff --git a/cmd/docker-trust/trust/common.go b/cmd/docker-trust/trust/common.go index f05e577273..f839bd26bd 100644 --- a/cmd/docker-trust/trust/common.go +++ b/cmd/docker-trust/trust/common.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "fmt" + "slices" "sort" "strings" @@ -110,9 +111,6 @@ func lookupTrustInfo(ctx context.Context, cli command.Cli, remote string) ([]tru } func formatAdminRole(roleWithSigs client.RoleWithSignatures) string { - adminKeyList := roleWithSigs.KeyIDs - sort.Strings(adminKeyList) - var role string switch roleWithSigs.Name { case data.CanonicalTargetsRole: @@ -122,6 +120,7 @@ func formatAdminRole(roleWithSigs client.RoleWithSignatures) string { default: return "" } + adminKeyList := slices.Sorted(slices.Values(roleWithSigs.KeyIDs)) return fmt.Sprintf("%s:\t%s\n", role, strings.Join(adminKeyList, ", ")) } diff --git a/cmd/docker-trust/trust/formatter.go b/cmd/docker-trust/trust/formatter.go index 13e5712a27..2bde36115f 100644 --- a/cmd/docker-trust/trust/formatter.go +++ b/cmd/docker-trust/trust/formatter.go @@ -1,7 +1,7 @@ package trust import ( - "sort" + "slices" "strings" "github.com/docker/cli/cli/command/formatter" @@ -73,8 +73,7 @@ func (c *trustTagContext) Digest() string { // Signers returns the sorted list of entities who signed this tag func (c *trustTagContext) Signers() string { - sort.Strings(c.s.Signers) - return strings.Join(c.s.Signers, ", ") + return strings.Join(slices.Sorted(slices.Values(c.s.Signers)), ", ") } // signerInfoWrite writes the context. @@ -108,15 +107,13 @@ type signerInfoContext struct { // Keys returns the sorted list of keys associated with the signer func (c *signerInfoContext) Keys() string { - sort.Strings(c.s.Keys) - truncatedKeys := []string{} + keys := slices.Sorted(slices.Values(c.s.Keys)) if c.trunc { - for _, keyID := range c.s.Keys { - truncatedKeys = append(truncatedKeys, formatter.TruncateID(keyID)) + for i, keyID := range keys { + keys[i] = formatter.TruncateID(keyID) } - return strings.Join(truncatedKeys, ", ") } - return strings.Join(c.s.Keys, ", ") + return strings.Join(keys, ", ") } // Signer returns the name of the signer diff --git a/cmd/docker-trust/trust/inspect.go b/cmd/docker-trust/trust/inspect.go index fabb1d7a13..74240b61ad 100644 --- a/cmd/docker-trust/trust/inspect.go +++ b/cmd/docker-trust/trust/inspect.go @@ -4,10 +4,11 @@ package trust import ( + "cmp" "context" "encoding/json" "fmt" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -80,36 +81,38 @@ func getRepoTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) } } - signerList, adminList := []trustSigner{}, []trustSigner{} - - signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles) - - for signerName, signerKeys := range signerRoleToKeyIDs { - signerKeyList := []trustKey{} + var signerList []trustSigner + for signerName, signerKeys := range getDelegationRoleToKeyMap(delegationRoles) { + signerKeyList := make([]trustKey, 0, len(signerKeys)) for _, keyID := range signerKeys { signerKeyList = append(signerKeyList, trustKey{ID: keyID}) } signerList = append(signerList, trustSigner{signerName, signerKeyList}) } - sort.Slice(signerList, func(i, j int) bool { return signerList[i].Name > signerList[j].Name }) + // Sort by name in descending order. + slices.SortFunc(signerList, func(a, b trustSigner) int { return cmp.Compare(b.Name, a.Name) }) + var adminList []trustSigner for _, adminRole := range adminRolesWithSigs { + var name string switch adminRole.Name { case data.CanonicalRootRole: - rootKeys := []trustKey{} - for _, keyID := range adminRole.KeyIDs { - rootKeys = append(rootKeys, trustKey{ID: keyID}) - } - adminList = append(adminList, trustSigner{"Root", rootKeys}) + name = "Root" case data.CanonicalTargetsRole: - targetKeys := []trustKey{} - for _, keyID := range adminRole.KeyIDs { - targetKeys = append(targetKeys, trustKey{ID: keyID}) - } - adminList = append(adminList, trustSigner{"Repository", targetKeys}) + name = "Repository" + default: + continue } + + keys := make([]trustKey, 0, len(adminRole.KeyIDs)) + for _, keyID := range adminRole.KeyIDs { + keys = append(keys, trustKey{ID: keyID}) + } + adminList = append(adminList, trustSigner{name, keys}) } - sort.Slice(adminList, func(i, j int) bool { return adminList[i].Name > adminList[j].Name }) + + // Sort by name in descending order. + slices.SortFunc(adminList, func(a, b trustSigner) int { return cmp.Compare(b.Name, a.Name) }) return json.Marshal(trustRepo{ Name: remote, diff --git a/cmd/docker-trust/trust/inspect_pretty.go b/cmd/docker-trust/trust/inspect_pretty.go index 3eac2cfbe8..68e717be71 100644 --- a/cmd/docker-trust/trust/inspect_pretty.go +++ b/cmd/docker-trust/trust/inspect_pretty.go @@ -1,9 +1,11 @@ package trust import ( + "cmp" "context" "fmt" "io" + "slices" "sort" "github.com/docker/cli/cli/command" @@ -44,7 +46,10 @@ func prettyPrintTrustInfo(ctx context.Context, dockerCLI command.Cli, remote str } func printSortedAdminKeys(out io.Writer, adminRoles []client.RoleWithSignatures) { - sort.Slice(adminRoles, func(i, j int) bool { return adminRoles[i].Name > adminRoles[j].Name }) + // Sort by name in descending order. + slices.SortFunc(adminRoles, func(a, b client.RoleWithSignatures) int { + return cmp.Compare(b.Name, a.Name) + }) for _, adminRole := range adminRoles { if formattedAdminRole := formatAdminRole(adminRole); formattedAdminRole != "" { _, _ = fmt.Fprintf(out, " %s", formattedAdminRole) diff --git a/cmd/docker-trust/trust/sign.go b/cmd/docker-trust/trust/sign.go index 6b4b933f72..f965ffb86a 100644 --- a/cmd/docker-trust/trust/sign.go +++ b/cmd/docker-trust/trust/sign.go @@ -6,7 +6,7 @@ import ( "fmt" "io" "path" - "sort" + "slices" "strings" "github.com/distribution/reference" @@ -189,8 +189,8 @@ func getExistingSignatureInfoForReleasedTag(notaryRepo notaryclient.Repository, } func prettyPrintExistingSignatureInfo(out io.Writer, existingSigInfo trustTagRow) { - sort.Strings(existingSigInfo.Signers) - joinedSigners := strings.Join(existingSigInfo.Signers, ", ") + signers := slices.Sorted(slices.Values(existingSigInfo.Signers)) + joinedSigners := strings.Join(signers, ", ") _, _ = fmt.Fprintf(out, "Existing signatures for tag %s digest %s from:\n%s\n", existingSigInfo.SignedTag, existingSigInfo.Digest, joinedSigners) } @@ -228,8 +228,7 @@ func getOrGenerateNotaryKey(notaryRepo notaryclient.Repository, role data.RoleNa var key data.PublicKey // always select the first key by ID if len(keys) > 0 { - sort.Strings(keys) - keyID := keys[0] + keyID := slices.Min(keys) privKey, _, err := notaryRepo.GetCryptoService().GetPrivateKey(keyID) if err != nil { return nil, err From ed406be52e35c795964cd06e8881def90214b0a9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 13:28:34 +0200 Subject: [PATCH 21/44] cli/command: modernize with slices and maps packages Signed-off-by: Sebastiaan van Stijn --- cli/command/completion/functions_test.go | 7 ++- cli/command/container/create_test.go | 8 +-- cli/command/container/restart_test.go | 7 ++- cli/command/container/rm_test.go | 8 ++- cli/command/container/stop_test.go | 7 ++- cli/command/formatter/buildcache.go | 29 +++++----- cli/command/formatter/container.go | 32 +++++----- .../image/build/internal/git/gitutils.go | 6 +- cli/command/image/build_test.go | 4 +- cli/command/image/push_test.go | 8 ++- cli/command/service/formatter.go | 22 ++++--- cli/command/service/logs.go | 7 ++- cli/command/service/opts.go | 10 ++-- cli/command/service/update.go | 58 +++++++------------ cli/command/service/update_test.go | 6 +- cli/command/stack/loader.go | 6 +- cli/command/stack/remove.go | 16 ++--- cli/command/system/events.go | 17 +++--- cli/command/system/info.go | 6 +- cli/command/system/version.go | 13 ++--- cli/command/volume/create_test.go | 9 ++- 21 files changed, 140 insertions(+), 146 deletions(-) diff --git a/cli/command/completion/functions_test.go b/cli/command/completion/functions_test.go index bb2fa1b62d..87706005c9 100644 --- a/cli/command/completion/functions_test.go +++ b/cli/command/completion/functions_test.go @@ -3,9 +3,9 @@ package completion import ( "context" "errors" - "sort" "testing" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/image" "github.com/moby/moby/api/types/network" @@ -177,9 +177,10 @@ func TestCompleteEnvVarNames(t *testing.T) { values, directives := EnvVarNames()(nil, nil, "") assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion") - sort.Strings(values) expected := []string{"ENV_A", "ENV_B"} - assert.Check(t, is.DeepEqual(values, expected)) + assert.Check(t, is.DeepEqual(values, expected, cmpopts.SortSlices(func(a, b string) bool { + return a < b + }))) } func TestCompleteFileNames(t *testing.T) { diff --git a/cli/command/container/create_test.go b/cli/command/container/create_test.go index 1c6ec92649..9c0099904e 100644 --- a/cli/command/container/create_test.go +++ b/cli/command/container/create_test.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" "runtime" - "sort" "strings" "testing" @@ -15,6 +14,7 @@ import ( "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/system" "github.com/moby/moby/client" @@ -284,12 +284,12 @@ func TestCreateContainerWithProxyConfig(t *testing.T) { "ALL_PROXY=allProxy", "all_proxy=allProxy", } - sort.Strings(expected) fakeCLI := test.NewFakeCli(&fakeClient{ createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) { - sort.Strings(options.Config.Env) - assert.DeepEqual(t, options.Config.Env, expected) + assert.DeepEqual(t, options.Config.Env, expected, cmpopts.SortSlices(func(a, b string) bool { + return a < b + })) return client.ContainerCreateResult{}, nil }, }) diff --git a/cli/command/container/restart_test.go b/cli/command/container/restart_test.go index 569571b717..4754446866 100644 --- a/cli/command/container/restart_test.go +++ b/cli/command/container/restart_test.go @@ -4,11 +4,11 @@ import ( "context" "errors" "io" - "sort" "sync" "testing" "github.com/docker/cli/internal/test" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/client" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -87,8 +87,9 @@ func TestRestart(t *testing.T) { } else { assert.Check(t, is.Nil(err)) } - sort.Strings(restarted) - assert.Check(t, is.DeepEqual(restarted, tc.restarted)) + assert.Check(t, is.DeepEqual(restarted, tc.restarted, cmpopts.SortSlices(func(a, b string) bool { + return a < b + }))) }) } } diff --git a/cli/command/container/rm_test.go b/cli/command/container/rm_test.go index 4ab14407bb..5e31e9abbc 100644 --- a/cli/command/container/rm_test.go +++ b/cli/command/container/rm_test.go @@ -4,11 +4,11 @@ import ( "context" "errors" "io" - "sort" "sync" "testing" "github.com/docker/cli/internal/test" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/client" "gotest.tools/v3/assert" ) @@ -53,8 +53,10 @@ func TestRemoveForce(t *testing.T) { assert.NilError(t, err) } assert.Equal(t, cli.ErrBuffer().String(), "") - sort.Strings(removed) - assert.DeepEqual(t, removed, []string{"mycontainer", "nosuchcontainer"}) + expected := []string{"mycontainer", "nosuchcontainer"} + assert.DeepEqual(t, removed, expected, cmpopts.SortSlices(func(a, b string) bool { + return a < b + })) }) } } diff --git a/cli/command/container/stop_test.go b/cli/command/container/stop_test.go index ed7fc335e2..484e7b4f39 100644 --- a/cli/command/container/stop_test.go +++ b/cli/command/container/stop_test.go @@ -4,11 +4,11 @@ import ( "context" "errors" "io" - "sort" "sync" "testing" "github.com/docker/cli/internal/test" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/client" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -88,8 +88,9 @@ func TestStop(t *testing.T) { } else { assert.Check(t, is.Nil(err)) } - sort.Strings(stopped) - assert.Check(t, is.DeepEqual(stopped, tc.stopped)) + assert.Check(t, is.DeepEqual(stopped, tc.stopped, cmpopts.SortSlices(func(a, b string) bool { + return a < b + }))) }) } } diff --git a/cli/command/formatter/buildcache.go b/cli/command/formatter/buildcache.go index 3a3c349988..d63cf05d63 100644 --- a/cli/command/formatter/buildcache.go +++ b/cli/command/formatter/buildcache.go @@ -1,7 +1,10 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package formatter import ( - "sort" + "slices" "strconv" "strings" "time" @@ -52,20 +55,20 @@ shared: {{.Shared}} } func buildCacheSort(buildCache []build.CacheRecord) { - sort.Slice(buildCache, func(i, j int) bool { - lui, luj := buildCache[i].LastUsedAt, buildCache[j].LastUsedAt + slices.SortFunc(buildCache, func(a, b build.CacheRecord) int { switch { - case lui == nil && luj == nil: - return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0 - case lui == nil: - return true - case luj == nil: - return false - case lui.Equal(*luj): - return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0 - default: - return lui.Before(*luj) + case a.LastUsedAt == nil && b.LastUsedAt == nil: + return strings.Compare(a.ID, b.ID) + case a.LastUsedAt == nil: + return -1 + case b.LastUsedAt == nil: + return 1 } + + if c := a.LastUsedAt.Compare(*b.LastUsedAt); c != 0 { + return c + } + return strings.Compare(a.ID, b.ID) }) } diff --git a/cli/command/formatter/container.go b/cli/command/formatter/container.go index b65209c26c..7b77a8a753 100644 --- a/cli/command/formatter/container.go +++ b/cli/command/formatter/container.go @@ -4,9 +4,10 @@ package formatter import ( + "cmp" "fmt" "net" - "sort" + "slices" "strconv" "strings" "time" @@ -295,7 +296,7 @@ func (c *ContainerContext) Labels() string { for k, v := range c.c.Labels { joinLabels = append(joinLabels, k+"="+v) } - sort.Strings(joinLabels) + slices.Sort(joinLabels) return strings.Join(joinLabels, ",") } @@ -395,9 +396,7 @@ func DisplayablePorts(ports []container.PortSummary) string { var result []string var hostMappings []string var groupMapKeys []string - sort.Slice(ports, func(i, j int) bool { - return comparePorts(ports[i], ports[j]) - }) + slices.SortFunc(ports, comparePorts) for _, port := range ports { current := port.PrivatePort @@ -452,18 +451,13 @@ func formGroup(key string, start, last uint16) string { return group + "/" + groupType } -func comparePorts(i, j container.PortSummary) bool { - if i.PrivatePort != j.PrivatePort { - return i.PrivatePort < j.PrivatePort - } - - if i.IP != j.IP { - return i.IP.Less(j.IP) - } - - if i.PublicPort != j.PublicPort { - return i.PublicPort < j.PublicPort - } - - return i.Type < j.Type +// comparePorts compares ports by private port, IP address, public port, +// and protocol, in that order. +func comparePorts(a, b container.PortSummary) int { + return cmp.Or( + cmp.Compare(a.PrivatePort, b.PrivatePort), + a.IP.Compare(b.IP), + cmp.Compare(a.PublicPort, b.PublicPort), + cmp.Compare(a.Type, b.Type), + ) } diff --git a/cli/command/image/build/internal/git/gitutils.go b/cli/command/image/build/internal/git/gitutils.go index e9245cb098..82b7188324 100644 --- a/cli/command/image/build/internal/git/gitutils.go +++ b/cli/command/image/build/internal/git/gitutils.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package git import ( @@ -7,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "strings" "github.com/moby/sys/symlink" @@ -202,7 +206,7 @@ func (repo gitRepo) checkout(root string) (string, error) { } func (repo gitRepo) gitWithinDir(dir string, args ...string) ([]byte, error) { - args = append([]string{"-c", "protocol.file.allow=never"}, args...) // Block sneaky repositories from using repos from the filesystem as submodules. + args = slices.Concat([]string{"-c", "protocol.file.allow=never"}, args) // Block sneaky repositories from using repos from the filesystem as submodules. cmd := exec.Command("git", args...) cmd.Dir = dir // Disable unsafe remote protocols. diff --git a/cli/command/image/build_test.go b/cli/command/image/build_test.go index 88d94d6cde..1d61d05bbe 100644 --- a/cli/command/image/build_test.go +++ b/cli/command/image/build_test.go @@ -8,7 +8,7 @@ import ( "io" "os" "path/filepath" - "sort" + "slices" "testing" "github.com/docker/cli/cli/streams" @@ -211,6 +211,6 @@ func (f *fakeBuild) filenames(t *testing.T) []string { for _, header := range h { names = append(names, header.Name) } - sort.Strings(names) + slices.Sort(names) return names } diff --git a/cli/command/image/push_test.go b/cli/command/image/push_test.go index e0e0195e6a..3dbca45ca9 100644 --- a/cli/command/image/push_test.go +++ b/cli/command/image/push_test.go @@ -102,9 +102,11 @@ func TestRunPushRespectsNoColorForAuxNotes(t *testing.T) { SelectedManifest: ocispec.Descriptor{Digest: "sha256:2222222222222222222222222222222222222222222222222222222222222222"}, }) assert.NilError(t, err) - line := append([]byte(`{"aux":`), aux...) - line = append(line, '}', '\n') - return fakeStreamResult{ReadCloser: io.NopCloser(bytes.NewReader(line))}, nil + var buf bytes.Buffer + buf.WriteString(`{"aux":`) + buf.Write(aux) + buf.WriteString("}\n") + return fakeStreamResult{ReadCloser: io.NopCloser(&buf)}, nil }, }) cli.Out().SetIsTerminal(true) diff --git a/cli/command/service/formatter.go b/cli/command/service/formatter.go index 1ec5263deb..5b77bbb1a5 100644 --- a/cli/command/service/formatter.go +++ b/cli/command/service/formatter.go @@ -1,8 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package service import ( + "cmp" "errors" "fmt" + "slices" "sort" "strconv" "strings" @@ -775,17 +780,16 @@ func (c *serviceContext) Ports() string { return "" } - pr := portRange{} - ports := []string{} - - servicePorts := c.service.Endpoint.Ports - sort.Slice(servicePorts, func(i, j int) bool { - if servicePorts[i].Protocol == servicePorts[j].Protocol { - return servicePorts[i].PublishedPort < servicePorts[j].PublishedPort - } - return servicePorts[i].Protocol < servicePorts[j].Protocol + // Sort by protocol first, then by published port. + slices.SortFunc(c.service.Endpoint.Ports, func(a, b swarm.PortConfig) int { + return cmp.Or( + cmp.Compare(a.Protocol, b.Protocol), + cmp.Compare(a.PublishedPort, b.PublishedPort), + ) }) + var pr portRange + var ports []string for _, p := range c.service.Endpoint.Ports { if p.PublishMode == swarm.PortConfigPublishModeIngress { prIsRange := pr.tEnd != pr.tStart diff --git a/cli/command/service/logs.go b/cli/command/service/logs.go index 3474423325..79908b8657 100644 --- a/cli/command/service/logs.go +++ b/cli/command/service/logs.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package service import ( @@ -6,7 +9,7 @@ import ( "errors" "fmt" "io" - "sort" + "slices" "strconv" "strings" @@ -314,7 +317,7 @@ func (lw *logWriter) Write(buf []byte) (int, error) { d = append(d, k+"="+details[k]) } // then sort em - sort.Strings(d) + slices.Sort(d) // then join and append output = append(output, []byte(strings.Join(d, ","))...) output = append(output, ' ') diff --git a/cli/command/service/opts.go b/cli/command/service/opts.go index e04f392e46..e2758e64cd 100644 --- a/cli/command/service/opts.go +++ b/cli/command/service/opts.go @@ -4,12 +4,12 @@ package service import ( + "cmp" "context" "errors" "fmt" "net/netip" "slices" - "sort" "strconv" "strings" "time" @@ -737,15 +737,15 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N } networks := convertNetworks(options.networks) - for i, net := range networks { - nwID, err := resolveNetworkID(ctx, apiClient, net.Target) + for i := range networks { + nwID, err := resolveNetworkID(ctx, apiClient, networks[i].Target) if err != nil { return service, err } networks[i].Target = nwID } - sort.Slice(networks, func(i, j int) bool { - return networks[i].Target < networks[j].Target + slices.SortFunc(networks, func(a, b swarm.NetworkAttachmentConfig) int { + return cmp.Compare(a.Target, b.Target) }) resources, err := options.resources.ToResourceRequirements(flags) diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 7520b08891..f3f7df4314 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -4,13 +4,13 @@ package service import ( + "cmp" "context" "errors" "fmt" "maps" "net/netip" "slices" - "sort" "strings" "time" @@ -635,7 +635,7 @@ func updatePlacementConstraints(flags *pflag.FlagSet, placement *swarm.Placement } } // Sort so that result is predictable. - sort.Strings(newConstraints) + slices.Sort(newConstraints) placement.Constraints = newConstraints } @@ -720,7 +720,7 @@ func updateSysCtls(flags *pflag.FlagSet, field *map[string]string) { } func updateUlimits(flags *pflag.FlagSet, ulimits []*container.Ulimit) []*container.Ulimit { - newUlimits := make(map[string]*container.Ulimit) + newUlimits := make(map[string]*container.Ulimit, len(ulimits)) for _, ulimit := range ulimits { newUlimits[ulimit.Name] = ulimit @@ -737,17 +737,9 @@ func updateUlimits(flags *pflag.FlagSet, ulimits []*container.Ulimit) []*contain newUlimits[ulimit.Name] = ulimit } } - if len(newUlimits) == 0 { - return nil - } - limits := make([]*container.Ulimit, 0, len(newUlimits)) - for _, ulimit := range newUlimits { - limits = append(limits, ulimit) - } - sort.SliceStable(limits, func(i, j int) bool { - return limits[i].Name < limits[j].Name + return slices.SortedFunc(maps.Values(newUlimits), func(a, b *container.Ulimit) int { + return cmp.Compare(a.Name, b.Name) }) - return limits } func updateEnvironment(flags *pflag.FlagSet, field *[]string) { @@ -953,14 +945,12 @@ func updateMounts(flags *pflag.FlagSet, mounts *[]mount.Mount) error { newMounts = append(newMounts, mnt) } } - sort.Slice(newMounts, func(i, j int) bool { - a, b := newMounts[i], newMounts[j] - - if a.Source == b.Source { - return a.Target < b.Target - } - - return a.Source < b.Source + // Sort mounts by source, then by target. + slices.SortFunc(newMounts, func(a, b mount.Mount) int { + return cmp.Or( + cmp.Compare(a.Source, b.Source), + cmp.Compare(a.Target, b.Target), + ) }) *mounts = newMounts return nil @@ -980,7 +970,7 @@ func updateGroups(flags *pflag.FlagSet, groups *[]string) error { } } // Sort so that result is predictable. - sort.Strings(newGroups) + slices.Sort(newGroups) *groups = newGroups return nil @@ -1039,7 +1029,7 @@ func updateDNSConfig(flags *pflag.FlagSet, config **swarm.DNSConfig) error { } } // Sort so that result is predictable. - sort.Strings(newConfig.Search) + slices.Sort(newConfig.Search) options := (*config).Options if flags.Changed(flagDNSOptionAdd) { @@ -1054,7 +1044,7 @@ func updateDNSConfig(flags *pflag.FlagSet, config **swarm.DNSConfig) error { } } // Sort so that result is predictable. - sort.Strings(newConfig.Options) + slices.Sort(newConfig.Options) *config = newConfig return nil @@ -1107,12 +1097,8 @@ portLoop: } } - // Sort the PortConfig to avoid unnecessary updates - sort.Slice(newPorts, func(i, j int) bool { - // We convert PortConfig into `port/protocol`, e.g., `80/tcp` - // In updatePorts we already filter out with map so there is duplicate entries - return portConfigToString(&newPorts[i]) < portConfigToString(&newPorts[j]) - }) + // Sort the PortConfig to avoid unnecessary updates. + slices.SortFunc(newPorts, swarm.PortConfig.Compare) *portConfig = newPorts return nil } @@ -1348,8 +1334,8 @@ func updateNetworks(ctx context.Context, apiClient client.NetworkAPIClient, flag } } - sort.Slice(newNetworks, func(i, j int) bool { - return newNetworks[i].Target < newNetworks[j].Target + slices.SortFunc(newNetworks, func(a, b swarm.NetworkAttachmentConfig) int { + return cmp.Compare(a.Target, b.Target) }) spec.TaskTemplate.Networks = newNetworks @@ -1523,10 +1509,6 @@ func capsList(caps map[string]bool) []string { if caps[opts.AllCapabilities] { return []string{opts.AllCapabilities} } - out := make([]string, 0, len(caps)) - for c := range caps { - out = append(out, c) - } - sort.Strings(out) - return out + + return slices.Sorted(maps.Keys(caps)) } diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index 6158820951..1211360bcb 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -4,7 +4,7 @@ import ( "context" "fmt" "net/netip" - "sort" + "slices" "strconv" "testing" "time" @@ -141,7 +141,7 @@ func TestUpdateEnvironment(t *testing.T) { updateEnvironment(flags, &envs) assert.Assert(t, is.Len(envs, 2)) // Order has been removed in updateEnvironment (map) - sort.Strings(envs) + slices.Sort(envs) assert.Check(t, is.Equal("toadd=newenv", envs[0])) assert.Check(t, is.Equal("tokeep=value", envs[1])) } @@ -282,7 +282,7 @@ func TestUpdatePorts(t *testing.T) { assert.Assert(t, is.Len(portConfigs, 2)) // Do a sort to have the order (might have changed by map) targetPorts := []int{int(portConfigs[0].TargetPort), int(portConfigs[1].TargetPort)} - sort.Ints(targetPorts) + slices.Sort(targetPorts) assert.Check(t, is.Equal(555, targetPorts[0])) assert.Check(t, is.Equal(1000, targetPorts[1])) } diff --git a/cli/command/stack/loader.go b/cli/command/stack/loader.go index 49366e4d50..10579a08d8 100644 --- a/cli/command/stack/loader.go +++ b/cli/command/stack/loader.go @@ -10,7 +10,7 @@ import ( "os" "path/filepath" "runtime" - "sort" + "slices" "strings" "github.com/distribution/reference" @@ -75,9 +75,9 @@ func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]any { func propertyWarnings(properties map[string]string) string { msgs := make([]string, 0, len(properties)) for name, description := range properties { - msgs = append(msgs, fmt.Sprintf("%s: %s", name, description)) + msgs = append(msgs, name+": "+description) } - sort.Strings(msgs) + slices.Sort(msgs) return strings.Join(msgs, "\n\n") } diff --git a/cli/command/stack/remove.go b/cli/command/stack/remove.go index 90ab2df29b..2f6d061b35 100644 --- a/cli/command/stack/remove.go +++ b/cli/command/stack/remove.go @@ -1,10 +1,14 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package stack import ( + "cmp" "context" "errors" "fmt" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -96,15 +100,11 @@ func runRemove(ctx context.Context, dockerCli command.Cli, opts removeOptions) e return errors.Join(errs...) } -func sortServiceByName(services []swarm.Service) func(i, j int) bool { - return func(i, j int) bool { - return services[i].Spec.Name < services[j].Spec.Name - } -} - func removeServices(ctx context.Context, dockerCLI command.Cli, services []swarm.Service) bool { + slices.SortFunc(services, func(a, b swarm.Service) int { + return cmp.Compare(a.Spec.Name, b.Spec.Name) + }) var hasError bool - sort.Slice(services, sortServiceByName(services)) for _, service := range services { _, _ = fmt.Fprintln(dockerCLI.Out(), "Removing service", service.Spec.Name) if _, err := dockerCLI.Client().ServiceRemove(ctx, service.ID, client.ServiceRemoveOptions{}); err != nil { diff --git a/cli/command/system/events.go b/cli/command/system/events.go index eb77c6c6b2..36264ef010 100644 --- a/cli/command/system/events.go +++ b/cli/command/system/events.go @@ -1,10 +1,14 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package system import ( "context" "fmt" "io" - "sort" + "maps" + "slices" "strings" "text/template" "time" @@ -133,18 +137,13 @@ func prettyPrintEvent(out io.Writer, event events.Message) error { _, _ = fmt.Fprintf(out, "%s %s %s", event.Type, event.Action, event.Actor.ID) if len(event.Actor.Attributes) > 0 { - keys := make([]string, 0, len(event.Actor.Attributes)) - for k := range event.Actor.Attributes { - keys = append(keys, k) - } - sort.Strings(keys) + keys := slices.Sorted(maps.Keys(event.Actor.Attributes)) attrs := make([]string, 0, len(keys)) for _, k := range keys { - v := event.Actor.Attributes[k] - attrs = append(attrs, k+"="+v) + attrs = append(attrs, k+"="+event.Actor.Attributes[k]) } _, _ = fmt.Fprintf(out, " (%s)", strings.Join(attrs, ", ")) } - _, _ = fmt.Fprint(out, "\n") + _, _ = fmt.Fprintln(out) return nil } diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 80a21629ec..fcc533c426 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" "io" - "sort" + "slices" "strings" "github.com/docker/cli/cli" @@ -468,11 +468,11 @@ func printSwarmInfo(output io.Writer, info system.Info) { } fprintln(output, " Node Address:", info.Swarm.NodeAddr) if len(info.Swarm.RemoteManagers) > 0 { - managers := []string{} + managers := make([]string, 0, len(info.Swarm.RemoteManagers)) for _, entry := range info.Swarm.RemoteManagers { managers = append(managers, entry.Addr) } - sort.Strings(managers) + slices.Sort(managers) fprintln(output, " Manager Addresses:") for _, entry := range managers { fprintf(output, " %s\n", entry) diff --git a/cli/command/system/version.go b/cli/command/system/version.go index b385ef7181..41b7de87c9 100644 --- a/cli/command/system/version.go +++ b/cli/command/system/version.go @@ -1,11 +1,15 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package system import ( "context" "fmt" "io" + "maps" "runtime" - "sort" + "slices" "strconv" "text/template" "time" @@ -252,10 +256,5 @@ func newVersionTemplate(templateFormat string) (*template.Template, error) { } func getDetailsOrder(v system.ComponentVersion) []string { - out := make([]string, 0, len(v.Details)) - for k := range v.Details { - out = append(out, k) - } - sort.Strings(out) - return out + return slices.Sorted(maps.Keys(v.Details)) } diff --git a/cli/command/volume/create_test.go b/cli/command/volume/create_test.go index 7f8a2c387c..3f9e43cdc0 100644 --- a/cli/command/volume/create_test.go +++ b/cli/command/volume/create_test.go @@ -8,11 +8,11 @@ import ( "fmt" "io" "maps" - "sort" "strings" "testing" "github.com/docker/cli/internal/test" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/moby/moby/api/types/volume" "github.com/moby/moby/client" "gotest.tools/v3/assert" @@ -230,10 +230,9 @@ func TestVolumeCreateClusterOpts(t *testing.T) { cli := test.NewFakeCli(&fakeClient{ volumeCreateFunc: func(options client.VolumeCreateOptions) (client.VolumeCreateResult, error) { - sort.SliceStable(options.ClusterVolumeSpec.Secrets, func(i, j int) bool { - return options.ClusterVolumeSpec.Secrets[i].Key < options.ClusterVolumeSpec.Secrets[j].Key - }) - assert.DeepEqual(t, options, expectedOptions) + assert.Check(t, is.DeepEqual(options, expectedOptions, cmpopts.SortSlices(func(a, b volume.Secret) bool { + return a.Key < b.Key + }))) return client.VolumeCreateResult{}, nil }, }) From 33d586d81f1e4f00b1a737852680d97d2cb5b03c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 13:28:52 +0200 Subject: [PATCH 22/44] cli/compose: modernize with slices and maps packages Signed-off-by: Sebastiaan van Stijn --- cli/compose/convert/service.go | 22 ++++++++++--------- cli/compose/loader/interpolate.go | 23 +++++++------------- cli/compose/loader/loader.go | 36 +++++++++---------------------- cli/compose/loader/loader_test.go | 16 ++++++-------- cli/compose/loader/merge.go | 17 ++++++++++----- 5 files changed, 48 insertions(+), 66 deletions(-) diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index 762bf144ff..d30597451a 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -11,7 +11,6 @@ import ( "net/netip" "os" "slices" - "sort" "strings" "time" @@ -235,8 +234,8 @@ func convertServiceNetworks( nets = append(nets, netAttachConfig) } - sort.Slice(nets, func(i, j int) bool { - return nets[i].Target < nets[j].Target + slices.SortFunc(nets, func(a, b swarm.NetworkAttachmentConfig) int { + return cmp.Compare(a.Target, b.Target) }) return nets, nil } @@ -276,8 +275,10 @@ func convertServiceSecrets( return nil, err } // sort to ensure idempotence (don't restart services just because the entries are in different order) - sort.SliceStable(secrs, func(i, j int) bool { return secrs[i].SecretName < secrs[j].SecretName }) - return secrs, err + slices.SortStableFunc(secrs, func(a, b *swarm.SecretReference) int { + return cmp.Compare(a.SecretName, b.SecretName) + }) + return secrs, nil } // convertServiceConfigObjs takes an API client, a namespace, a ServiceConfig, @@ -352,8 +353,10 @@ func convertServiceConfigObjs( return nil, err } // sort to ensure idempotence (don't restart services just because the entries are in different order) - sort.SliceStable(confs, func(i, j int) bool { return confs[i].ConfigName < confs[j].ConfigName }) - return confs, err + slices.SortStableFunc(confs, func(a, b *swarm.ConfigReference) int { + return cmp.Compare(a.ConfigName, b.ConfigName) + }) + return confs, nil } type swarmReferenceTarget struct { @@ -609,8 +612,7 @@ func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortC // convertEnvironment converts key/value mappings to a slice, and sorts // the results. func convertEnvironment(source map[string]*string) []string { - var output []string - + output := make([]string, 0, len(source)) for name, value := range source { switch value { case nil: @@ -619,7 +621,7 @@ func convertEnvironment(source map[string]*string) []string { output = append(output, name+"="+*value) } } - sort.Strings(output) + slices.Sort(output) return output } diff --git a/cli/compose/loader/interpolate.go b/cli/compose/loader/interpolate.go index 11c44113d4..ef84ea10a4 100644 --- a/cli/compose/loader/interpolate.go +++ b/cli/compose/loader/interpolate.go @@ -5,6 +5,7 @@ package loader import ( "fmt" + "slices" "strconv" "strings" @@ -35,20 +36,16 @@ var interpolateTypeCastMapping = map[interp.Path]interp.Cast{ servicePath("tty"): toBoolean, servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean, servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean, - iPath("networks", interp.PathMatchAll, "external"): toBoolean, - iPath("networks", interp.PathMatchAll, "internal"): toBoolean, - iPath("networks", interp.PathMatchAll, "attachable"): toBoolean, - iPath("volumes", interp.PathMatchAll, "external"): toBoolean, - iPath("secrets", interp.PathMatchAll, "external"): toBoolean, - iPath("configs", interp.PathMatchAll, "external"): toBoolean, -} - -func iPath(parts ...string) interp.Path { - return interp.NewPath(parts...) + interp.NewPath("networks", interp.PathMatchAll, "external"): toBoolean, + interp.NewPath("networks", interp.PathMatchAll, "internal"): toBoolean, + interp.NewPath("networks", interp.PathMatchAll, "attachable"): toBoolean, + interp.NewPath("volumes", interp.PathMatchAll, "external"): toBoolean, + interp.NewPath("secrets", interp.PathMatchAll, "external"): toBoolean, + interp.NewPath("configs", interp.PathMatchAll, "external"): toBoolean, } func servicePath(parts ...string) interp.Path { - return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...) + return interp.NewPath(slices.Concat([]string{"services", interp.PathMatchAll}, parts)...) } func toInt(value string) (any, error) { @@ -70,7 +67,3 @@ func toBoolean(value string) (any, error) { return nil, fmt.Errorf("invalid boolean: %s", value) } } - -func interpolateConfig(configDict map[string]any, opts interp.Options) (map[string]any, error) { - return interp.Interpolate(configDict, opts) -} diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index 91d85cfd0a..0a9cb03ae0 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -10,7 +10,7 @@ import ( "path" "path/filepath" "reflect" - "sort" + "slices" "strconv" "strings" "time" @@ -111,7 +111,7 @@ func Load(configDetails types.ConfigDetails, opt ...func(*Options)) (*types.Conf } if !options.SkipInterpolation { - configDict, err = interpolateConfig(configDict, *options.Interpolate) + configDict, err = interp.Interpolate(configDict, *options.Interpolate) if err != nil { return nil, err } @@ -231,16 +231,7 @@ func GetUnsupportedProperties(configDicts ...map[string]any) []string { } } - return sortedKeys(unsupported) -} - -func sortedKeys(set map[string]bool) []string { - keys := make([]string, 0, len(set)) - for key := range set { - keys = append(keys, key) - } - sort.Strings(keys) - return keys + return slices.Sorted(maps.Keys(unsupported)) } // GetDeprecatedProperties returns the list of any deprecated properties that @@ -827,8 +818,7 @@ func transformListOrMapping(listOrMapping any, sep string, allowNil bool, allowS result := make([]string, 0, len(value)) for _, entry := range value { for i, allowSep := range allowSeps { - entry := fmt.Sprint(entry) - k, v, ok := strings.Cut(entry, allowSep) + k, v, ok := strings.Cut(fmt.Sprint(entry), allowSep) if ok { // Entry uses this allowed separator. Add it to the result, using // sep as a separator. @@ -885,7 +875,7 @@ var transformShellCommand TransformerFunc = func(value any) (any, error) { var transformHealthCheckTest TransformerFunc = func(data any) (any, error) { switch value := data.(type) { case string: - return append([]string{"CMD-SHELL"}, value), nil + return []string{"CMD-SHELL", value}, nil case []any: return value, nil default: @@ -925,16 +915,10 @@ func toServicePortConfigs(value string) ([]any, error) { return nil, err } // We need to sort the key of the ports to make sure it is consistent - keys := make([]string, 0, len(ports)) - for port := range ports { - keys = append(keys, string(port)) - } - sort.Strings(keys) - var portConfigs []any - for _, key := range keys { + for _, key := range slices.Sorted(maps.Keys(ports)) { // Reuse ConvertPortToPortConfig so that it is consistent - port, err := network.ParsePort(key) + port, err := network.ParsePort(string(key)) if err != nil { return nil, err } @@ -975,13 +959,13 @@ func toString(value any, allowNil bool) any { } func toStringList(value map[string]any, separator string, allowNil bool) []string { - output := []string{} + output := make([]string, 0, len(value)) for key, value := range value { if value == nil && !allowNil { continue } - output = append(output, fmt.Sprintf("%s%s%s", key, separator, value)) + output = append(output, fmt.Sprintf("%s%s%v", key, separator, value)) } - sort.Strings(output) + slices.Sort(output) return output } diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index 1b97ffaf2d..41f70c7cb2 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -7,7 +7,6 @@ import ( "bytes" "os" "runtime" - "sort" "testing" "github.com/docker/cli/cli/compose/types" @@ -236,7 +235,9 @@ func TestLoad(t *testing.T) { actual, err := Load(buildConfigDetails(sampleDict, nil)) assert.NilError(t, err) assert.Check(t, is.Equal(sampleConfig.Version, actual.Version)) - assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services))) + assert.Check(t, is.DeepEqual(sampleConfig.Services, actual.Services, cmpopts.SortSlices(func(a, b types.ServiceConfig) bool { + return a.Name < b.Name + }))) assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks)) assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } @@ -310,7 +311,9 @@ services: func TestParseAndLoad(t *testing.T) { actual, err := loadYAML(sampleYAML) assert.NilError(t, err) - assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services))) + assert.Check(t, is.DeepEqual(sampleConfig.Services, actual.Services, cmpopts.SortSlices(func(a, b types.ServiceConfig) bool { + return a.Name < b.Name + }))) assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks)) assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } @@ -1191,13 +1194,6 @@ services: assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size: must be an integer") } -func serviceSort(services []types.ServiceConfig) []types.ServiceConfig { - sort.Slice(services, func(i, j int) bool { - return services[i].Name < services[j].Name - }) - return services -} - func TestLoadAttachableNetwork(t *testing.T) { config, err := loadYAML(` version: "3.2" diff --git a/cli/compose/loader/merge.go b/cli/compose/loader/merge.go index 4b397a2bd6..cf666ab0b2 100644 --- a/cli/compose/loader/merge.go +++ b/cli/compose/loader/merge.go @@ -9,7 +9,6 @@ import ( "fmt" "reflect" "slices" - "sort" "dario.cat/mergo" "github.com/docker/cli/cli/compose/types" @@ -152,7 +151,9 @@ func toServiceSecretConfigsSlice(dst reflect.Value, m map[any]any) error { for _, v := range m { s = append(s, v.(types.ServiceSecretConfig)) } - sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) + slices.SortFunc(s, func(a, b types.ServiceSecretConfig) int { + return cmp.Compare(a.Source, b.Source) + }) dst.Set(reflect.ValueOf(s)) return nil } @@ -162,7 +163,9 @@ func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[any]any) error { for _, v := range m { s = append(s, v.(types.ServiceConfigObjConfig)) } - sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) + slices.SortFunc(s, func(a, b types.ServiceConfigObjConfig) int { + return cmp.Compare(a.Source, b.Source) + }) dst.Set(reflect.ValueOf(s)) return nil } @@ -172,7 +175,9 @@ func toServicePortConfigsSlice(dst reflect.Value, m map[any]any) error { for _, v := range m { s = append(s, v.(types.ServicePortConfig)) } - sort.Slice(s, func(i, j int) bool { return s[i].Published < s[j].Published }) + slices.SortFunc(s, func(a, b types.ServicePortConfig) int { + return cmp.Compare(a.Published, b.Published) + }) dst.Set(reflect.ValueOf(s)) return nil } @@ -182,7 +187,9 @@ func toServiceVolumeConfigsSlice(dst reflect.Value, m map[any]any) error { for _, v := range m { s = append(s, v.(types.ServiceVolumeConfig)) } - sort.Slice(s, func(i, j int) bool { return s[i].Target < s[j].Target }) + slices.SortFunc(s, func(a, b types.ServiceVolumeConfig) int { + return cmp.Compare(a.Target, b.Target) + }) dst.Set(reflect.ValueOf(s)) return nil } From 6ea81b3e2ea869753158f180b2901f0b0526d047 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 13:29:16 +0200 Subject: [PATCH 23/44] templates: modernize with slices and maps packages Signed-off-by: Sebastiaan van Stijn --- templates/templates.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/templates.go b/templates/templates.go index 24f29c179f..5443d70d7c 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -8,7 +8,7 @@ import ( "encoding/json" "fmt" "reflect" - "sort" + "slices" "strings" "text/template" ) @@ -125,12 +125,12 @@ func joinElements(elems any, sep string) (string, error) { return b.String(), nil case reflect.Map: - var out []string + out := make([]string, 0, rv.Len()) for _, k := range rv.MapKeys() { out = append(out, fmt.Sprint(rv.MapIndex(k).Interface())) } // Not ideal, but trying to keep a consistent order - sort.Strings(out) + slices.Sort(out) return strings.Join(out, sep), nil default: From b1780e06a098bf085273a6a5265c55210ecbdbe9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 13:29:35 +0200 Subject: [PATCH 24/44] opts: modernize with slices and maps packages Signed-off-by: Sebastiaan van Stijn --- opts/capabilities.go | 9 ++++++--- opts/ulimit.go | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/opts/capabilities.go b/opts/capabilities.go index 82d071853b..96a0e7d0f4 100644 --- a/opts/capabilities.go +++ b/opts/capabilities.go @@ -1,7 +1,10 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package opts import ( - "sort" + "slices" "strings" ) @@ -82,8 +85,8 @@ func EffectiveCapAddCapDrop(add, drop []string) (capAdd, capDrop []string) { } } - sort.Strings(capAdd) - sort.Strings(capDrop) + slices.Sort(capAdd) + slices.Sort(capDrop) return capAdd, capDrop } diff --git a/opts/ulimit.go b/opts/ulimit.go index aa88bce71a..ba4a82c9d8 100644 --- a/opts/ulimit.go +++ b/opts/ulimit.go @@ -1,8 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package opts import ( "fmt" - "sort" + "maps" + "slices" + "strings" "github.com/docker/go-units" "github.com/moby/moby/api/types/container" @@ -41,20 +46,15 @@ func (o *UlimitOpt) String() string { for _, v := range *o.values { out = append(out, v.String()) } - sort.Strings(out) - return fmt.Sprintf("%v", out) + slices.Sort(out) + return fmt.Sprint(out) } // GetList returns a slice of pointers to Ulimits. Values are sorted by name. func (o *UlimitOpt) GetList() []*container.Ulimit { - ulimits := make([]*container.Ulimit, 0, len(*o.values)) - for _, v := range *o.values { - ulimits = append(ulimits, v) - } - sort.SliceStable(ulimits, func(i, j int) bool { - return ulimits[i].Name < ulimits[j].Name + return slices.SortedFunc(maps.Values(*o.values), func(a, b *container.Ulimit) int { + return strings.Compare(a.Name, b.Name) }) - return ulimits } // Type returns the option type From 7631c672089ea48d5f72feb01c5b182c0394ad56 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 13:30:07 +0200 Subject: [PATCH 25/44] e2e: modernize Signed-off-by: Sebastiaan van Stijn --- e2e/stack/deploy_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/stack/deploy_test.go b/e2e/stack/deploy_test.go index daf7ccc220..166236936e 100644 --- a/e2e/stack/deploy_test.go +++ b/e2e/stack/deploy_test.go @@ -1,10 +1,10 @@ package stack import ( - "sort" "strings" "testing" + "github.com/google/go-cmp/cmp/cmpopts" "gotest.tools/v3/assert" "gotest.tools/v3/golden" "gotest.tools/v3/icmd" @@ -21,7 +21,7 @@ func TestDeployWithNamedResources(t *testing.T) { result.Assert(t, icmd.Success) stdout := strings.Split(result.Stdout(), "\n") expected := strings.Split(string(golden.Get(t, "stack-deploy-with-names.golden")), "\n") - sort.Strings(stdout) - sort.Strings(expected) - assert.DeepEqual(t, stdout, expected) + assert.DeepEqual(t, stdout, expected, cmpopts.SortSlices(func(a, b string) bool { + return a < b + })) } From 64484430f41f1d828d769f40a8f585dd82b4456b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 21:37:43 +0200 Subject: [PATCH 26/44] cli-plugins: modernize with slices and maps packages Signed-off-by: Sebastiaan van Stijn --- cli-plugins/manager/cobra_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli-plugins/manager/cobra_test.go b/cli-plugins/manager/cobra_test.go index 7089bdccb9..0de070fefd 100644 --- a/cli-plugins/manager/cobra_test.go +++ b/cli-plugins/manager/cobra_test.go @@ -3,6 +3,7 @@ package manager import ( "os" "path/filepath" + "slices" "sync" "testing" @@ -45,7 +46,7 @@ func TestPluginStubCompletionRestoresOSArgs(t *testing.T) { t.Cleanup(func() { os.Args = savedArgs }) originalArgs := []string{"docker", "image", "ls"} - os.Args = append([]string(nil), originalArgs...) + os.Args = slices.Clone(originalArgs) _, directive := cmd.ValidArgsFunction(cmd, []string{"--all"}, "alp") assert.Equal(t, directive, cobra.ShellCompDirectiveError) From 977d9993820d4facd87953a96d3680ba13e27f9c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 21:39:01 +0200 Subject: [PATCH 27/44] cli/config: use filepath.IsLocal to validate config paths Use filepath.Rel and filepath.IsLocal instead of comparing path prefixes as strings when checking that a path stays within the config directory. Also avoid calling Dir multiple times when constructing and validating the path. Signed-off-by: Sebastiaan van Stijn --- cli/config/config.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cli/config/config.go b/cli/config/config.go index 5a63780509..a7ea0acdd7 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -7,7 +7,6 @@ import ( "os/user" "path/filepath" "runtime" - "strings" "sync" "github.com/docker/cli/cli/config/configfile" @@ -98,9 +97,11 @@ func SetDir(dir string) { // Path returns the path to a file relative to the config dir func Path(p ...string) (string, error) { - path := filepath.Join(append([]string{Dir()}, p...)...) - if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) { - return "", fmt.Errorf("path %q is outside of root config directory %q", path, Dir()) + root := Dir() + path := filepath.Join(append([]string{root}, p...)...) + + if rel, err := filepath.Rel(root, path); err != nil || !filepath.IsLocal(rel) { + return "", fmt.Errorf("path %q is outside of root config directory %q", path, root) } return path, nil } From e480e6c6fc80f7545a290c5be9bdd3aa00a3dc16 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 2 Sep 2026 01:00:33 +0200 Subject: [PATCH 28/44] volume create: simplify cluster volume options Extract construction of the cluster volume spec from runCreate, and use struct literals for the individual cluster volume options. Also simplify topology parsing by reusing ConvertKVStringsToMap, use slices.SortFunc for deterministic secret ordering, and use max to clamp negative capacity values to zero. Signed-off-by: Sebastiaan van Stijn --- cli/command/volume/create.go | 144 +++++++++++++++-------------------- 1 file changed, 60 insertions(+), 84 deletions(-) diff --git a/cli/command/volume/create.go b/cli/command/volume/create.go index 5c2098e035..80b09ed209 100644 --- a/cli/command/volume/create.go +++ b/cli/command/volume/create.go @@ -4,10 +4,11 @@ package volume import ( + "cmp" "context" "errors" "fmt" - "sort" + "slices" "strings" "github.com/docker/cli/cli" @@ -117,89 +118,13 @@ func hasClusterVolumeOptionSet(flags *pflag.FlagSet) bool { } func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions) error { - volOpts := client.VolumeCreateOptions{ - Driver: options.driver, - DriverOpts: options.driverOpts.GetAll(), - Name: options.name, - Labels: opts.ConvertKVStringsToMap(options.labels.GetSlice()), - } - if options.cluster { - volOpts.ClusterVolumeSpec = &volume.ClusterVolumeSpec{ - Group: options.group, - AccessMode: &volume.AccessMode{ - Scope: volume.Scope(options.scope), - Sharing: volume.SharingMode(options.sharing), - }, - Availability: volume.Availability(options.availability), - } - - switch options.accessType { - case "mount": - volOpts.ClusterVolumeSpec.AccessMode.MountVolume = &volume.TypeMount{} - case "block": - volOpts.ClusterVolumeSpec.AccessMode.BlockVolume = &volume.TypeBlock{} - } - - vcr := &volume.CapacityRange{} - if r := options.requiredBytes.Value(); r >= 0 { - vcr.RequiredBytes = r - } - - if l := options.limitBytes.Value(); l >= 0 { - vcr.LimitBytes = l - } - volOpts.ClusterVolumeSpec.CapacityRange = vcr - - for key, secret := range options.secrets.GetAll() { - volOpts.ClusterVolumeSpec.Secrets = append( - volOpts.ClusterVolumeSpec.Secrets, - volume.Secret{ - Key: key, - Secret: secret, - }, - ) - } - sort.SliceStable(volOpts.ClusterVolumeSpec.Secrets, func(i, j int) bool { - return volOpts.ClusterVolumeSpec.Secrets[i].Key < volOpts.ClusterVolumeSpec.Secrets[j].Key - }) - - // TODO(dperny): ignore if no topology specified - topology := &volume.TopologyRequirement{} - for _, top := range options.requisiteTopology.GetSlice() { - // each topology takes the form segment=value,segment=value - // comma-separated list of equal separated maps - segments := map[string]string{} - for segment := range strings.SplitSeq(top, ",") { - // TODO(dperny): validate topology syntax - k, v, _ := strings.Cut(segment, "=") - segments[k] = v - } - topology.Requisite = append( - topology.Requisite, - volume.Topology{Segments: segments}, - ) - } - - for _, top := range options.preferredTopology.GetSlice() { - // each topology takes the form segment=value,segment=value - // comma-separated list of equal separated maps - segments := map[string]string{} - for segment := range strings.SplitSeq(top, ",") { - // TODO(dperny): validate topology syntax - k, v, _ := strings.Cut(segment, "=") - segments[k] = v - } - - topology.Preferred = append( - topology.Preferred, - volume.Topology{Segments: segments}, - ) - } - - volOpts.ClusterVolumeSpec.AccessibilityRequirements = topology - } - - res, err := dockerCli.Client().VolumeCreate(ctx, volOpts) + res, err := dockerCli.Client().VolumeCreate(ctx, client.VolumeCreateOptions{ + Driver: options.driver, + DriverOpts: options.driverOpts.GetAll(), + Name: options.name, + Labels: opts.ConvertKVStringsToMap(options.labels.GetSlice()), + ClusterVolumeSpec: clusterVolumeSpec(options), + }) if err != nil { return err } @@ -207,3 +132,54 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions _, _ = fmt.Fprintln(dockerCli.Out(), res.Volume.Name) return nil } + +func clusterVolumeSpec(options createOptions) *volume.ClusterVolumeSpec { + if !options.cluster { + return nil + } + + var secrets []volume.Secret + for key, secret := range options.secrets.GetAll() { + secrets = append(secrets, volume.Secret{Key: key, Secret: secret}) + } + slices.SortFunc(secrets, func(a, b volume.Secret) int { + return cmp.Compare(a.Key, b.Key) + }) + + accessMode := &volume.AccessMode{ + Scope: volume.Scope(options.scope), + Sharing: volume.SharingMode(options.sharing), + } + switch options.accessType { + case "mount": + accessMode.MountVolume = &volume.TypeMount{} + case "block": + accessMode.BlockVolume = &volume.TypeBlock{} + } + + return &volume.ClusterVolumeSpec{ + Group: options.group, + AccessMode: accessMode, + AccessibilityRequirements: &volume.TopologyRequirement{ + Requisite: parseTopologies(options.requisiteTopology.GetSlice()), + Preferred: parseTopologies(options.preferredTopology.GetSlice()), + }, + CapacityRange: &volume.CapacityRange{ + RequiredBytes: max(options.requiredBytes.Value(), 0), + LimitBytes: max(options.limitBytes.Value(), 0), + }, + Secrets: secrets, + Availability: volume.Availability(options.availability), + } +} + +func parseTopologies(values []string) []volume.Topology { + topologies := make([]volume.Topology, 0, len(values)) + for _, top := range values { + // TODO(dperny): validate topology syntax + topologies = append(topologies, volume.Topology{ + Segments: opts.ConvertKVStringsToMap(strings.Split(top, ",")), + }) + } + return topologies +} From 3cb9f1cb8692d0b92fa3e53e0f10895d9f21860f Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 1 Sep 2026 23:52:21 +0200 Subject: [PATCH 29/44] vendor: github.com/fvbommel/sortorder v1.2.0 full diff: https://github.com/fvbommel/sortorder/compare/v1.1.0...v1.2.0 Signed-off-by: Sebastiaan van Stijn --- vendor.mod | 2 +- vendor.sum | 4 +-- .../github.com/fvbommel/sortorder/natsort.go | 34 +++++++++++++++---- vendor/modules.txt | 4 +-- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/vendor.mod b/vendor.mod index 8b1affe793..46be786ffc 100644 --- a/vendor.mod +++ b/vendor.mod @@ -23,7 +23,7 @@ require ( github.com/docker/docker-credential-helpers v0.9.9 github.com/docker/go-connections v0.8.1 github.com/docker/go-units v0.5.0 - github.com/fvbommel/sortorder v1.1.0 + github.com/fvbommel/sortorder v1.2.0 github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/gogo/protobuf v1.3.2 diff --git a/vendor.sum b/vendor.sum index c1e8e5b293..cab284654f 100644 --- a/vendor.sum +++ b/vendor.sum @@ -49,8 +49,8 @@ github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= -github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= -github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fvbommel/sortorder v1.2.0 h1:TRIiRiGX+djh3Yf4FVxmWmAcYfIr5dH0NbzJWOSAWZk= +github.com/fvbommel/sortorder v1.2.0/go.mod h1:LbhO04ijZIeUuvz9B9BkI/qYrpZZEn1gWhxv4QjUKVs= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= diff --git a/vendor/github.com/fvbommel/sortorder/natsort.go b/vendor/github.com/fvbommel/sortorder/natsort.go index e4f15110b8..5eba2bc6f5 100644 --- a/vendor/github.com/fvbommel/sortorder/natsort.go +++ b/vendor/github.com/fvbommel/sortorder/natsort.go @@ -1,5 +1,7 @@ package sortorder +import "cmp" + // Natural implements sort.Interface to sort strings in natural order. This // means that e.g. "abc2" < "abc12". // @@ -25,18 +27,36 @@ func isDigit(b byte) bool { return '0' <= b && b <= '9' } // // Limitation: only ASCII digits (0-9) are considered. func NaturalLess(str1, str2 string) bool { + return NaturalCompare(str1, str2) < 0 +} + +// NaturalCompare compares str1 and str2 using the same natural ordering as +// [NaturalLess]. It returns a negative value if str1 sorts before str2, a +// positive value if str1 sorts after str2, and zero if str1 and str2 are equal. +// +// NaturalCompare is suitable for APIs that accept a three-way comparison +// function, such as [slices.SortFunc] and [slices.SortStableFunc]. +func NaturalCompare(str1, str2 string) int { idx1, idx2 := 0, 0 for idx1 < len(str1) && idx2 < len(str2) { c1, c2 := str1[idx1], str2[idx2] dig1, dig2 := isDigit(c1), isDigit(c2) switch { - case dig1 != dig2: // Digits before other characters. - return dig1 // True if LHS is a digit, false if the RHS is one. + case dig1 != dig2: + // The first difference is that one is a digit and the other is not. + // That means that one (possibly empty) non-digit string has ended, and the other has not. + // The one that has ended is ordered before the one that continues, + // for example: "ab1" < "abc1" after skipping the matching "ab" prefix. + // This means the side with the digit is ordered before the other one. + if dig1 { + return -1 + } + return 1 case !dig1: // && !dig2, because dig1 == dig2 // UTF-8 compares bytewise-lexicographically, no need to decode // codepoints. if c1 != c2 { - return c1 < c2 + return cmp.Compare(c1, c2) } idx1++ idx2++ @@ -55,22 +75,22 @@ func NaturalLess(str1, str2 string) bool { // If lengths of numbers with non-zero prefix differ, the shorter // one is less. if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 { - return len1 < len2 + return cmp.Compare(len1, len2) } // If they're equally long, string comparison is correct. if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 { - return nr1 < nr2 + return cmp.Compare(nr1, nr2) } // Otherwise, the one with less zeros is less. // Because everything up to the number is equal, comparing the index // after the zeros is sufficient. if nonZero1 != nonZero2 { - return nonZero1 < nonZero2 + return cmp.Compare(nonZero1, nonZero2) } } // They're identical so far, so continue comparing. } // So far they are identical. At least one is ended. If the other continues, // it sorts last. - return len(str1) < len(str2) + return cmp.Compare(len(str1), len(str2)) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 1a2ff3894f..39b5a29ce6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -91,8 +91,8 @@ github.com/docker/go-units # github.com/felixge/httpsnoop v1.1.0 ## explicit; go 1.25 github.com/felixge/httpsnoop -# github.com/fvbommel/sortorder v1.1.0 -## explicit; go 1.13 +# github.com/fvbommel/sortorder v1.2.0 +## explicit; go 1.21 github.com/fvbommel/sortorder # github.com/go-jose/go-jose/v4 v4.1.4 ## explicit; go 1.24.0 From 3014a557367a7f3a0a354bc6bc8263830657ff31 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 2 Sep 2026 02:54:58 +0200 Subject: [PATCH 30/44] cmd/docker-trust: update github.com/fvbommel/sortorder v1.2.0 Signed-off-by: Sebastiaan van Stijn --- cmd/docker-trust/go.mod | 2 +- cmd/docker-trust/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/docker-trust/go.mod b/cmd/docker-trust/go.mod index e29e424805..10d14f7f84 100644 --- a/cmd/docker-trust/go.mod +++ b/cmd/docker-trust/go.mod @@ -9,7 +9,7 @@ require ( github.com/docker/cli-docs-tool v0.11.0 github.com/docker/distribution v2.8.3+incompatible github.com/docker/go-connections v0.7.0 - github.com/fvbommel/sortorder v1.1.0 + github.com/fvbommel/sortorder v1.2.0 github.com/moby/moby/api v1.54.2 github.com/moby/moby/client v0.4.1 github.com/opencontainers/go-digest v1.0.0 diff --git a/cmd/docker-trust/go.sum b/cmd/docker-trust/go.sum index cbf1eca9b8..e4ab52452e 100644 --- a/cmd/docker-trust/go.sum +++ b/cmd/docker-trust/go.sum @@ -73,8 +73,8 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= -github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fvbommel/sortorder v1.2.0 h1:TRIiRiGX+djh3Yf4FVxmWmAcYfIr5dH0NbzJWOSAWZk= +github.com/fvbommel/sortorder v1.2.0/go.mod h1:LbhO04ijZIeUuvz9B9BkI/qYrpZZEn1gWhxv4QjUKVs= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= From c096bd1261885ab7c4fd61db17f752d3070d535a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 08:46:18 +0000 Subject: [PATCH 31/44] build(deps): bump the codeql-actions group with 3 updates Bumps the codeql-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.37.8 to 4.37.9 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938) Updates `github/codeql-action/autobuild` from 4.37.8 to 4.37.9 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938) Updates `github/codeql-action/analyze` from 4.37.8 to 4.37.9 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-actions - dependency-name: github/codeql-action/autobuild dependency-version: 4.37.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.37.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bb473da1c3..9541e83ac6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -68,14 +68,14 @@ jobs: cache: false - name: Initialize CodeQL - uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 + uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 + uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 + uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 with: category: "/language:go" From c24195d63f93662d50994e1361a58016a0c3db44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Wed, 2 Sep 2026 14:48:59 +0200 Subject: [PATCH 32/44] Update to go1.26.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- .github/workflows/codeql.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/validate.yml | 2 +- .golangci.yml | 2 +- Dockerfile | 2 +- dockerfiles/Dockerfile.dev | 2 +- dockerfiles/Dockerfile.lint | 2 +- dockerfiles/Dockerfile.vendor | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9541e83ac6..6069b6ae5c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -64,7 +64,7 @@ jobs: name: Update Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.26.7" + go-version: "1.26.8" cache: false - name: Initialize CodeQL diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 31bdcba08f..eafa8cce9b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,7 +68,7 @@ jobs: name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.26.7" + go-version: "1.26.8" cache: false - name: Test diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 50e388f91d..3532727ff9 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -101,7 +101,7 @@ jobs: name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.26.7" + go-version: "1.26.8" cache: false - name: Run gocompat check diff --git a/.golangci.yml b/.golangci.yml index 1d29a21434..ad5a06f1d8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,7 +5,7 @@ run: # which causes it to fallback to go1.17 semantics. # # TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24 - go: "1.26.7" + go: "1.26.8" timeout: 5m diff --git a/Dockerfile b/Dockerfile index a9070d805a..02d1fc7d7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine ARG ALPINE_VERSION=3.23 ARG BASE_DEBIAN_DISTRO=bookworm -ARG GO_VERSION=1.26.7 +ARG GO_VERSION=1.26.8 # XX_VERSION specifies the version of the xx utility to use. # It must be a valid tag in the docker.io/tonistiigi/xx image repository. diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev index 50d814be96..7f02956252 100644 --- a/dockerfiles/Dockerfile.dev +++ b/dockerfiles/Dockerfile.dev @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.26.7 +ARG GO_VERSION=1.26.8 # ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. # It must be a supported tag in the docker.io/library/alpine image repository diff --git a/dockerfiles/Dockerfile.lint b/dockerfiles/Dockerfile.lint index d78e0c674c..b0404ab8b8 100644 --- a/dockerfiles/Dockerfile.lint +++ b/dockerfiles/Dockerfile.lint @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.26.7 +ARG GO_VERSION=1.26.8 # ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. # It must be a supported tag in the docker.io/library/alpine image repository diff --git a/dockerfiles/Dockerfile.vendor b/dockerfiles/Dockerfile.vendor index f811b3fb55..d781ef9830 100644 --- a/dockerfiles/Dockerfile.vendor +++ b/dockerfiles/Dockerfile.vendor @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.26.7 +ARG GO_VERSION=1.26.8 # ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. # It must be a supported tag in the docker.io/library/alpine image repository From b07de476942d2e6f4b97927defabcef77e337823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 3 Sep 2026 10:31:30 +0200 Subject: [PATCH 33/44] cli/command: Avoid panics after client initialization errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When client.New rejects an invalid Docker host or other client option, it returns a nil concrete client with an error. Returning that pointer directly as client.APIClient stores a typed-nil interface, so lazy version checks dereference it. Return an error instead. Signed-off-by: Paweł Gronowski --- cli/command/cli.go | 6 +++++- cli/command/cli_test.go | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cli/command/cli.go b/cli/command/cli.go index f7f428b11b..e405c2cc4f 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -316,7 +316,11 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF opts = append(opts, withCustomHeaders) } opts = append(opts, extraOpts...) - return client.New(opts...) + apiClient, err := client.New(opts...) + if err != nil { + return nil, err + } + return apiClient, nil } func resolveDockerEndpoint(s store.Reader, contextName string) (docker.Endpoint, error) { diff --git a/cli/command/cli_test.go b/cli/command/cli_test.go index be5bcabfbb..27be8d348c 100644 --- a/cli/command/cli_test.go +++ b/cli/command/cli_test.go @@ -132,6 +132,15 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) { assert.Equal(t, apiclient.ClientVersion(), expectedVersion) } +func TestNewAPIClientFromFlagsWithInvalidAPIVersionFromEnv(t *testing.T) { + t.Setenv("DOCKER_API_VERSION", "1") + t.Setenv("DOCKER_HOST", ":2375") + + apiClient, err := NewAPIClientFromFlags(&flags.ClientOptions{}, &configfile.ConfigFile{}) + assert.ErrorContains(t, err, "invalid API version") + assert.Check(t, apiClient == nil) +} + type fakeClient struct { client.Client pingFunc func() (client.PingResult, error) From e54eb133cbc4ade4410d2bd59c3e50286ecd4c4a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 2 Sep 2026 02:24:46 +0200 Subject: [PATCH 34/44] update naturalsort sorting with slices Signed-off-by: Sebastiaan van Stijn --- cli-plugins/manager/manager.go | 9 ++++++--- cli/cobra.go | 9 ++++++--- cli/command/config/ls.go | 10 +++++++--- cli/command/container/port.go | 9 +++++---- cli/command/context/list.go | 6 +++--- cli/command/network/list.go | 10 +++++++--- cli/command/node/list.go | 10 +++++++--- cli/command/plugin/list.go | 10 +++++++--- cli/command/secret/ls.go | 10 +++++++--- cli/command/service/formatter.go | 5 ++--- cli/command/stack/list.go | 9 ++++++--- cli/command/stack/services.go | 10 +++++++--- cli/command/system/prune.go | 6 ++---- cli/command/task/print.go | 31 +++++++++++------------------- cli/command/volume/list.go | 10 +++++++--- cli/context/store/metadatastore.go | 6 +++--- 16 files changed, 93 insertions(+), 67 deletions(-) diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index bdbed4e023..bf398533aa 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package manager import ( @@ -6,7 +9,7 @@ import ( "os" "os/exec" "path/filepath" - "sort" + slices "slices" "strings" "sync" @@ -164,8 +167,8 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e return nil, err } - sort.Slice(plugins, func(i, j int) bool { - return sortorder.NaturalLess(plugins[i].Name, plugins[j].Name) + slices.SortFunc(plugins, func(a, b Plugin) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return plugins, nil diff --git a/cli/cobra.go b/cli/cobra.go index 4ec721f887..bfcb1bdcc8 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -1,9 +1,12 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package cli import ( "fmt" "os" - "sort" + "slices" "strings" "github.com/docker/cli/cli-plugins/metadata" @@ -273,8 +276,8 @@ func topCommands(cmd *cobra.Command) []*cobra.Command { cmds = append(cmds, sub) } } - sort.SliceStable(cmds, func(i, j int) bool { - return sortorder.NaturalLess(cmds[i].Annotations["category-top"], cmds[j].Annotations["category-top"]) + slices.SortStableFunc(cmds, func(a, b *cobra.Command) int { + return sortorder.NaturalCompare(a.Annotations["category-top"], b.Annotations["category-top"]) }) return cmds } diff --git a/cli/command/config/ls.go b/cli/command/config/ls.go index cbed96200e..94541fcb57 100644 --- a/cli/command/config/ls.go +++ b/cli/command/config/ls.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package config import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -62,8 +66,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name) + slices.SortFunc(res.Items, func(a, b swarm.Config) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) configCtx := formatter.Context{ diff --git a/cli/command/container/port.go b/cli/command/container/port.go index 534ddd1d34..839e40ca2f 100644 --- a/cli/command/container/port.go +++ b/cli/command/container/port.go @@ -1,10 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package container import ( "context" "fmt" "net" - "sort" + "slices" "strings" "github.com/docker/cli/cli" @@ -80,9 +83,7 @@ func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) erro } if len(out) > 0 { - sort.Slice(out, func(i, j int) bool { - return sortorder.NaturalLess(out[i], out[j]) - }) + slices.SortFunc(out, sortorder.NaturalCompare) _, _ = fmt.Fprintln(dockerCli.Out(), strings.Join(out, "\n")) } diff --git a/cli/command/context/list.go b/cli/command/context/list.go index 67e9b9c660..3507ae9c22 100644 --- a/cli/command/context/list.go +++ b/cli/command/context/list.go @@ -6,7 +6,7 @@ package context import ( "fmt" "os" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -101,8 +101,8 @@ func runList(dockerCli command.Cli, opts *listOptions) error { Error: errMsg, }) } - sort.Slice(contexts, func(i, j int) bool { - return sortorder.NaturalLess(contexts[i].Name, contexts[j].Name) + slices.SortFunc(contexts, func(a, b *formatter.ClientContext) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) if err := format(dockerCli, opts, contexts); err != nil { return err diff --git a/cli/command/network/list.go b/cli/command/network/list.go index 70d94ec786..9f2c90f459 100644 --- a/cli/command/network/list.go +++ b/cli/command/network/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package network import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/network" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -61,8 +65,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Name, res.Items[j].Name) + slices.SortFunc(res.Items, func(a, b network.Summary) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) networksCtx := formatter.Context{ diff --git a/cli/command/node/list.go b/cli/command/node/list.go index 526ec65587..48e5641f29 100644 --- a/cli/command/node/list.go +++ b/cli/command/node/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package node import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -73,8 +77,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er Output: dockerCLI.Out(), Format: newFormat(format, options.quiet), } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Description.Hostname, res.Items[j].Description.Hostname) + slices.SortFunc(res.Items, func(a, b swarm.Node) int { + return sortorder.NaturalCompare(a.Description.Hostname, b.Description.Hostname) }) return formatWrite(nodesCtx, res, info) } diff --git a/cli/command/plugin/list.go b/cli/command/plugin/list.go index fb651ab25a..3606ac5bbf 100644 --- a/cli/command/plugin/list.go +++ b/cli/command/plugin/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package plugin import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/plugin" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -54,8 +58,8 @@ func runList(ctx context.Context, dockerCli command.Cli, options listOptions) er return err } - sort.Slice(resp.Items, func(i, j int) bool { - return sortorder.NaturalLess(resp.Items[i].Name, resp.Items[j].Name) + slices.SortFunc(resp.Items, func(a, b plugin.Plugin) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) format := options.format diff --git a/cli/command/secret/ls.go b/cli/command/secret/ls.go index d68b52165c..999ea53e38 100644 --- a/cli/command/secret/ls.go +++ b/cli/command/secret/ls.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package secret import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -59,8 +63,8 @@ func runSecretList(ctx context.Context, dockerCLI command.Cli, options listOptio } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name) + slices.SortFunc(res.Items, func(a, b swarm.Secret) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) secretCtx := formatter.Context{ diff --git a/cli/command/service/formatter.go b/cli/command/service/formatter.go index 5b77bbb1a5..4440f6165d 100644 --- a/cli/command/service/formatter.go +++ b/cli/command/service/formatter.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "slices" - "sort" "strconv" "strings" "time" @@ -620,8 +619,8 @@ func NewListFormat(source string, quiet bool) formatter.Format { // ListFormatWrite writes the context func ListFormatWrite(ctx formatter.Context, services client.ServiceListResult) error { render := func(format func(subContext formatter.SubContext) error) error { - sort.Slice(services.Items, func(i, j int) bool { - return sortorder.NaturalLess(services.Items[i].Spec.Name, services.Items[j].Spec.Name) + slices.SortFunc(services.Items, func(a, b swarm.Service) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) for _, service := range services.Items { serviceCtx := &serviceContext{service: service} diff --git a/cli/command/stack/list.go b/cli/command/stack/list.go index 6461b09020..8463bd5515 100644 --- a/cli/command/stack/list.go +++ b/cli/command/stack/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package stack import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -52,8 +55,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, opts listOptions) error Output: dockerCLI.Out(), Format: format, } - sort.Slice(stacks, func(i, j int) bool { - return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name) + slices.SortFunc(stacks, func(a, b stackSummary) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return stackWrite(stackCtx, stacks) } diff --git a/cli/command/stack/services.go b/cli/command/stack/services.go index 26d252d98e..49bf573da7 100644 --- a/cli/command/stack/services.go +++ b/cli/command/stack/services.go @@ -1,9 +1,12 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package stack import ( "context" "fmt" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -12,6 +15,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" cliopts "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -68,8 +72,8 @@ func formatWrite(dockerCLI command.Cli, services client.ServiceListResult, opts _, _ = fmt.Fprintln(dockerCLI.Err(), "Nothing found in stack:", opts.namespace) return nil } - sort.Slice(services.Items, func(i, j int) bool { - return sortorder.NaturalLess(services.Items[i].Spec.Name, services.Items[j].Spec.Name) + slices.SortFunc(services.Items, func(a, b swarm.Service) int { + return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name) }) f := opts.format diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index 698b6d1bb3..33544bf5b5 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -8,7 +8,7 @@ import ( "context" "errors" "fmt" - "sort" + "slices" "text/template" "github.com/containerd/errdefs" @@ -172,9 +172,7 @@ func dryRun(ctx context.Context, dockerCli command.Cli, options pruneOptions) (s filters = append(filters, name+"="+v) } } - sort.Slice(filters, func(i, j int) bool { - return sortorder.NaturalLess(filters[i], filters[j]) - }) + slices.SortFunc(filters, sortorder.NaturalCompare) } var buffer bytes.Buffer diff --git a/cli/command/task/print.go b/cli/command/task/print.go index ae27d42d01..846a293cdc 100644 --- a/cli/command/task/print.go +++ b/cli/command/task/print.go @@ -1,9 +1,13 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package task import ( + "cmp" "context" "fmt" - "sort" + "slices" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/formatter" @@ -14,24 +18,6 @@ import ( "github.com/moby/moby/client" ) -type tasksSortable []swarm.Task - -func (t tasksSortable) Len() int { - return len(t) -} - -func (t tasksSortable) Swap(i, j int) { - t[i], t[j] = t[j], t[i] -} - -func (t tasksSortable) Less(i, j int) bool { - if t[i].Name != t[j].Name { - return sortorder.NaturalLess(t[i].Name, t[j].Name) - } - // Sort tasks for the same service and slot by most recent. - return t[j].Meta.CreatedAt.Before(t[i].CreatedAt) -} - // Print task information in a format. // Besides this, command `docker node ps ` // and `docker stack ps` will call this, too. @@ -44,7 +30,12 @@ func Print(ctx context.Context, dockerCli command.Cli, tasks client.TaskListResu // First sort tasks, so that all tasks (including previous ones) of the same // service and slot are together. This must be done first, to print "previous" // tasks indented - sort.Stable(tasksSortable(tasks.Items)) + slices.SortStableFunc(tasks.Items, func(a, b swarm.Task) int { + return cmp.Or( + sortorder.NaturalCompare(a.Name, b.Name), + b.Meta.CreatedAt.Compare(a.Meta.CreatedAt), + ) + }) names := map[string]string{} nodes := map[string]string{} diff --git a/cli/command/volume/list.go b/cli/command/volume/list.go index 1e0df1f84b..bab03f9f44 100644 --- a/cli/command/volume/list.go +++ b/cli/command/volume/list.go @@ -1,8 +1,11 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.26 + package volume import ( "context" - "sort" + "slices" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -10,6 +13,7 @@ import ( flagsHelper "github.com/docker/cli/cli/flags" "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" + "github.com/moby/moby/api/types/volume" "github.com/moby/moby/client" "github.com/spf13/cobra" ) @@ -85,8 +89,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er } } - sort.Slice(res.Items, func(i, j int) bool { - return sortorder.NaturalLess(res.Items[i].Name, res.Items[j].Name) + slices.SortFunc(res.Items, func(a, b volume.Volume) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) volumeCtx := formatter.Context{ diff --git a/cli/context/store/metadatastore.go b/cli/context/store/metadatastore.go index 8bada79fe5..1751e82069 100644 --- a/cli/context/store/metadatastore.go +++ b/cli/context/store/metadatastore.go @@ -10,7 +10,7 @@ import ( "os" "path/filepath" "reflect" - "sort" + "slices" "github.com/fvbommel/sortorder" "github.com/moby/sys/atomicwriter" @@ -122,8 +122,8 @@ func (s *metadataStore) list() ([]Metadata, error) { } res = append(res, c) } - sort.Slice(res, func(i, j int) bool { - return sortorder.NaturalLess(res[i].Name, res[j].Name) + slices.SortFunc(res, func(a, b Metadata) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return res, nil } From 36e01adcfa55166fd30a7b44c4b96d9d28a72774 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 2 Sep 2026 02:54:58 +0200 Subject: [PATCH 35/44] cmd/docker-trust: update naturalsort sorting with slices Signed-off-by: Sebastiaan van Stijn --- cmd/docker-trust/trust/common.go | 5 ++--- cmd/docker-trust/trust/inspect_pretty.go | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/docker-trust/trust/common.go b/cmd/docker-trust/trust/common.go index f839bd26bd..c743d49722 100644 --- a/cmd/docker-trust/trust/common.go +++ b/cmd/docker-trust/trust/common.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "slices" - "sort" "strings" "github.com/docker/cli/cli/command" @@ -163,8 +162,8 @@ func matchReleasedSignatures(allTargets []client.TargetSignedStruct) []trustTagR for targetKey, signers := range releasedTargetRows { signatureRows = append(signatureRows, trustTagRow{targetKey, signers}) } - sort.Slice(signatureRows, func(i, j int) bool { - return sortorder.NaturalLess(signatureRows[i].SignedTag, signatureRows[j].SignedTag) + slices.SortFunc(signatureRows, func(a, b trustTagRow) int { + return sortorder.NaturalCompare(a.SignedTag, b.SignedTag) }) return signatureRows } diff --git a/cmd/docker-trust/trust/inspect_pretty.go b/cmd/docker-trust/trust/inspect_pretty.go index 68e717be71..4336f0872a 100644 --- a/cmd/docker-trust/trust/inspect_pretty.go +++ b/cmd/docker-trust/trust/inspect_pretty.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "slices" - "sort" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/formatter" @@ -85,15 +84,15 @@ func printSignerInfo(out io.Writer, roleToKeyIDs map[string][]string) error { Format: defaultSignerInfoTableFormat, Trunc: true, } - formattedSignerInfo := []signerInfo{} + formattedSignerInfo := make([]signerInfo, 0, len(roleToKeyIDs)) for name, keyIDs := range roleToKeyIDs { formattedSignerInfo = append(formattedSignerInfo, signerInfo{ Name: name, Keys: keyIDs, }) } - sort.Slice(formattedSignerInfo, func(i, j int) bool { - return sortorder.NaturalLess(formattedSignerInfo[i].Name, formattedSignerInfo[j].Name) + slices.SortFunc(formattedSignerInfo, func(a, b signerInfo) int { + return sortorder.NaturalCompare(a.Name, b.Name) }) return signerInfoWrite(signerInfoCtx, formattedSignerInfo) } From cb6c7bc03fb18709c6354892f938d11020e85826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 3 Sep 2026 20:40:38 +0200 Subject: [PATCH 36/44] vendor: github.com/moby/moby/api v1.56.0-dev (b9b109e4d341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit full diff: https://github.com/moby/moby/compare/api/v1.55.0...b9b109e4d341 Signed-off-by: Paweł Gronowski --- vendor.mod | 2 +- vendor.sum | 4 ++-- .../moby/moby/api/types/container/hostconfig.go | 1 + .../moby/moby/api/types/image/image_inspect.go | 6 +++++- .../moby/api/types/plugin/plugin_responses.go | 17 ++++++++++------- vendor/modules.txt | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/vendor.mod b/vendor.mod index 6c909cf5b5..62ed16e733 100644 --- a/vendor.mod +++ b/vendor.mod @@ -33,7 +33,7 @@ require ( github.com/google/uuid v1.6.0 github.com/mattn/go-runewidth v0.0.28 github.com/moby/go-archive v0.3.3 - github.com/moby/moby/api v1.55.0 + github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 github.com/moby/moby/client v0.5.1 github.com/moby/patternmatcher v0.6.1 github.com/moby/swarmkit/v2 v2.1.2 diff --git a/vendor.sum b/vendor.sum index cab284654f..5d74480c98 100644 --- a/vendor.sum +++ b/vendor.sum @@ -88,8 +88,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME= github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= -github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc= -github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 h1:ooFZgSrQ/xRDfGves7imF3goa+32bEcJrcXIDlL/qpI= +github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw= github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= diff --git a/vendor/github.com/moby/moby/api/types/container/hostconfig.go b/vendor/github.com/moby/moby/api/types/container/hostconfig.go index 0f889c6512..297ebc35e5 100644 --- a/vendor/github.com/moby/moby/api/types/container/hostconfig.go +++ b/vendor/github.com/moby/moby/api/types/container/hostconfig.go @@ -454,6 +454,7 @@ type HostConfig struct { ShmSize int64 // Total shm memory usage Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container Runtime string `json:",omitempty"` // Runtime to use with this container + Umask *uint32 `json:",omitempty"` // Initial process umask // Applicable to Windows Isolation Isolation // Isolation technology of the container (e.g. default, hyperv) diff --git a/vendor/github.com/moby/moby/api/types/image/image_inspect.go b/vendor/github.com/moby/moby/api/types/image/image_inspect.go index df09c9511b..90ebfd894c 100644 --- a/vendor/github.com/moby/moby/api/types/image/image_inspect.go +++ b/vendor/github.com/moby/moby/api/types/image/image_inspect.go @@ -72,7 +72,11 @@ type InspectResponse struct { // run on (especially for Windows). OsVersion string `json:",omitempty"` - // Size is the total size of the image including all layers it is composed of. + // Size is the total size of the selected image variant, including all layers + // it is composed of. + // + // When using the containerd image store, this includes both the image content + // that's present locally and the unpacked snapshot data. Size int64 // GraphDriver holds information about the storage driver used to store the diff --git a/vendor/github.com/moby/moby/api/types/plugin/plugin_responses.go b/vendor/github.com/moby/moby/api/types/plugin/plugin_responses.go index 91b327eb47..f4b224307c 100644 --- a/vendor/github.com/moby/moby/api/types/plugin/plugin_responses.go +++ b/vendor/github.com/moby/moby/api/types/plugin/plugin_responses.go @@ -1,9 +1,5 @@ package plugin -import ( - "sort" -) - // ListResponse contains the response for the Engine API type ListResponse []Plugin @@ -15,19 +11,26 @@ type Privilege struct { Value []string } -// Privileges is a list of Privilege +// Privileges is a list of Privilege. type Privileges []Privilege +// Len implements [sort.Interface]. +// +// Deprecated: use [slices.SortFunc] to sort privileges instead. func (s Privileges) Len() int { return len(s) } +// Less implements [sort.Interface]. +// +// Deprecated: use [slices.SortFunc] to sort privileges instead. func (s Privileges) Less(i, j int) bool { return s[i].Name < s[j].Name } +// Swap implements [sort.Interface]. +// +// Deprecated: use [slices.SortFunc] to sort privileges instead. func (s Privileges) Swap(i, j int) { - sort.Strings(s[i].Value) - sort.Strings(s[j].Value) s[i], s[j] = s[j], s[i] } diff --git a/vendor/modules.txt b/vendor/modules.txt index 39b5a29ce6..a5a6f1918b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -168,7 +168,7 @@ github.com/moby/go-archive github.com/moby/go-archive/compression github.com/moby/go-archive/internal/archiveoptions github.com/moby/go-archive/tarheader -# github.com/moby/moby/api v1.55.0 +# github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 ## explicit; go 1.24 github.com/moby/moby/api/pkg/authconfig github.com/moby/moby/api/pkg/stdcopy From 91fc8ad4a294c84f3e2198152be49e8d1fb58d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 3 Sep 2026 20:52:14 +0200 Subject: [PATCH 37/44] vendor: github.com/moby/moby/client v0.5.2-dev (b9b109e4d341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit full diff: https://github.com/moby/moby/compare/client/v0.5.1...b9b109e4d341 Signed-off-by: Paweł Gronowski --- vendor.mod | 2 +- vendor.sum | 4 +- .../moby/moby/client/checkpoint_create.go | 6 +- vendor/github.com/moby/moby/client/client.go | 2 +- .../moby/moby/client/config_create.go | 2 +- .../moby/moby/client/config_update.go | 2 +- .../moby/moby/client/container_commit.go | 2 +- .../moby/moby/client/container_copy.go | 2 +- .../moby/moby/client/container_create.go | 67 +++++++++---------- .../moby/moby/client/container_exec.go | 11 ++- .../moby/moby/client/container_update.go | 13 ++-- vendor/github.com/moby/moby/client/hijack.go | 2 +- .../moby/moby/client/image_build.go | 2 +- .../moby/moby/client/image_import.go | 2 +- .../github.com/moby/moby/client/image_load.go | 5 +- .../github.com/moby/moby/client/image_pull.go | 2 +- .../github.com/moby/moby/client/image_push.go | 2 +- vendor/github.com/moby/moby/client/login.go | 2 +- .../moby/moby/client/network_connect.go | 2 +- .../moby/moby/client/network_create.go | 2 +- .../moby/moby/client/network_disconnect.go | 5 +- .../moby/moby/client/node_update.go | 2 +- .../client/pkg/jsonmessage/jsonmessage.go | 40 +++++++++-- .../moby/moby/client/plugin_create.go | 2 +- .../moby/moby/client/plugin_install.go | 11 ++- .../moby/moby/client/plugin_push.go | 5 +- .../github.com/moby/moby/client/plugin_set.go | 2 +- .../moby/moby/client/plugin_upgrade.go | 7 +- vendor/github.com/moby/moby/client/request.go | 28 ++++---- .../moby/moby/client/secret_create.go | 2 +- .../moby/moby/client/secret_update.go | 2 +- .../moby/moby/client/service_create.go | 2 +- .../moby/moby/client/service_update.go | 2 +- .../github.com/moby/moby/client/swarm_init.go | 6 +- .../github.com/moby/moby/client/swarm_join.go | 6 +- .../moby/moby/client/swarm_unlock.go | 5 +- .../moby/moby/client/swarm_update.go | 2 +- vendor/github.com/moby/moby/client/utils.go | 8 +++ .../moby/moby/client/volume_create.go | 5 +- .../moby/moby/client/volume_update.go | 2 +- vendor/modules.txt | 2 +- 41 files changed, 143 insertions(+), 137 deletions(-) diff --git a/vendor.mod b/vendor.mod index 62ed16e733..9a51eb9201 100644 --- a/vendor.mod +++ b/vendor.mod @@ -34,7 +34,7 @@ require ( github.com/mattn/go-runewidth v0.0.28 github.com/moby/go-archive v0.3.3 github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 - github.com/moby/moby/client v0.5.1 + github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 github.com/moby/patternmatcher v0.6.1 github.com/moby/swarmkit/v2 v2.1.2 github.com/moby/sys/atomicwriter v0.1.0 diff --git a/vendor.sum b/vendor.sum index 5d74480c98..91237332b9 100644 --- a/vendor.sum +++ b/vendor.sum @@ -90,8 +90,8 @@ github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 h1:ooFZgSrQ/xRDfGves7imF3goa+32bEcJrcXIDlL/qpI= github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw= -github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= +github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 h1:/XUuyJxH/HXe5MBcQo+YG9hAtZTQNpWFeJ5PcxL+hs8= +github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/swarmkit/v2 v2.1.2 h1:1WDZAI6HVYNKdCG4zlXnTAPyLsLwuhRGWlHoOUf5Z6I= diff --git a/vendor/github.com/moby/moby/client/checkpoint_create.go b/vendor/github.com/moby/moby/client/checkpoint_create.go index b3ba5459d0..46867643f2 100644 --- a/vendor/github.com/moby/moby/client/checkpoint_create.go +++ b/vendor/github.com/moby/moby/client/checkpoint_create.go @@ -24,13 +24,11 @@ func (cli *Client) CheckpointCreate(ctx context.Context, containerID string, opt if err != nil { return CheckpointCreateResult{}, err } - requestBody := checkpoint.CreateRequest{ + resp, err := cli.post(ctx, "/containers/"+containerID+"/checkpoints", nil, nil, checkpoint.CreateRequest{ CheckpointID: options.CheckpointID, CheckpointDir: options.CheckpointDir, Exit: options.Exit, - } - - resp, err := cli.post(ctx, "/containers/"+containerID+"/checkpoints", nil, requestBody, nil) + }) defer ensureReaderClosed(resp) return CheckpointCreateResult{}, err } diff --git a/vendor/github.com/moby/moby/client/client.go b/vendor/github.com/moby/moby/client/client.go index 4b4ef976a3..3dbcacdeb9 100644 --- a/vendor/github.com/moby/moby/client/client.go +++ b/vendor/github.com/moby/moby/client/client.go @@ -109,7 +109,7 @@ const DummyHost = "api.moby.localhost" // overriding the version and disable API-version negotiation. // // This version may be lower than the version of the api library module used. -const MaxAPIVersion = "1.55" +const MaxAPIVersion = "1.56" // MinAPIVersion is the minimum API version supported by the client. API versions // below this version are not considered when performing API-version negotiation. diff --git a/vendor/github.com/moby/moby/client/config_create.go b/vendor/github.com/moby/moby/client/config_create.go index 874e2c947c..3c37ea9e50 100644 --- a/vendor/github.com/moby/moby/client/config_create.go +++ b/vendor/github.com/moby/moby/client/config_create.go @@ -19,7 +19,7 @@ type ConfigCreateResult struct { // ConfigCreate creates a new config. func (cli *Client) ConfigCreate(ctx context.Context, options ConfigCreateOptions) (ConfigCreateResult, error) { - resp, err := cli.post(ctx, "/configs/create", nil, options.Spec, nil) + resp, err := cli.post(ctx, "/configs/create", nil, nil, options.Spec) defer ensureReaderClosed(resp) if err != nil { return ConfigCreateResult{}, err diff --git a/vendor/github.com/moby/moby/client/config_update.go b/vendor/github.com/moby/moby/client/config_update.go index 31bdd79569..bfbac46dec 100644 --- a/vendor/github.com/moby/moby/client/config_update.go +++ b/vendor/github.com/moby/moby/client/config_update.go @@ -24,7 +24,7 @@ func (cli *Client) ConfigUpdate(ctx context.Context, id string, options ConfigUp } query := url.Values{} query.Set("version", options.Version.String()) - resp, err := cli.post(ctx, "/configs/"+id+"/update", query, options.Spec, nil) + resp, err := cli.post(ctx, "/configs/"+id+"/update", query, nil, options.Spec) defer ensureReaderClosed(resp) if err != nil { return ConfigUpdateResult{}, err diff --git a/vendor/github.com/moby/moby/client/container_commit.go b/vendor/github.com/moby/moby/client/container_commit.go index f73cd22f51..47981bec72 100644 --- a/vendor/github.com/moby/moby/client/container_commit.go +++ b/vendor/github.com/moby/moby/client/container_commit.go @@ -65,7 +65,7 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti } var response container.CommitResponse - resp, err := cli.post(ctx, "/commit", query, options.Config, nil) + resp, err := cli.post(ctx, "/commit", query, nil, options.Config) defer ensureReaderClosed(resp) if err != nil { return ContainerCommitResult{}, err diff --git a/vendor/github.com/moby/moby/client/container_copy.go b/vendor/github.com/moby/moby/client/container_copy.go index b37d1765f4..b91babd3bd 100644 --- a/vendor/github.com/moby/moby/client/container_copy.go +++ b/vendor/github.com/moby/moby/client/container_copy.go @@ -77,7 +77,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID string, opti query.Set("copyUIDGID", "true") } - response, err := cli.putRaw(ctx, "/containers/"+containerID+"/archive", query, options.Content, nil) + response, err := cli.putRaw(ctx, "/containers/"+containerID+"/archive", query, nil, options.Content) defer ensureReaderClosed(response) if err != nil { return CopyToContainerResult{}, err diff --git a/vendor/github.com/moby/moby/client/container_create.go b/vendor/github.com/moby/moby/client/container_create.go index d941a37207..7e6b15a96e 100644 --- a/vendor/github.com/moby/moby/client/container_create.go +++ b/vendor/github.com/moby/moby/client/container_create.go @@ -5,7 +5,7 @@ import ( "encoding/json" "net/url" "path" - "sort" + "slices" "strings" cerrdefs "github.com/containerd/errdefs" @@ -35,13 +35,6 @@ func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateO return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config.Image or Image is required") } - var response container.CreateResponse - - if options.HostConfig != nil { - options.HostConfig.CapAdd = normalizeCapabilities(options.HostConfig.CapAdd) - options.HostConfig.CapDrop = normalizeCapabilities(options.HostConfig.CapDrop) - } - query := url.Values{} if options.Platform != nil { if p := formatPlatform(*options.Platform); p != "unknown" { @@ -53,18 +46,17 @@ func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateO query.Set("name", options.Name) } - body := container.CreateRequest{ + resp, err := cli.post(ctx, "/containers/create", query, nil, container.CreateRequest{ Config: cfg, - HostConfig: options.HostConfig, + HostConfig: normalizeHostConfig(options.HostConfig), NetworkingConfig: options.NetworkingConfig, - } - - resp, err := cli.post(ctx, "/containers/create", query, body, nil) + }) defer ensureReaderClosed(resp) if err != nil { return ContainerCreateResult{}, err } + var response container.CreateResponse err = json.NewDecoder(resp.Body).Decode(&response) return ContainerCreateResult{ID: response.ID, Warnings: response.Warnings}, err } @@ -86,6 +78,17 @@ func formatPlatform(platform ocispec.Platform) string { // allCapabilities is a magic value for "all capabilities" const allCapabilities = "ALL" +// normalizeCap normalizes a capability to its canonical format by upper-casing +// and adding a "CAP_" prefix (if not yet present). It also accepts the "ALL" +// magic-value. +func normalizeCap(c string) string { + c = strings.ToUpper(c) + if c != allCapabilities && !strings.HasPrefix(c, "CAP_") { + c = "CAP_" + c + } + return c +} + // normalizeCapabilities normalizes capabilities to their canonical form, // removes duplicates, and sorts the results. // @@ -94,32 +97,22 @@ const allCapabilities = "ALL" // // [caps.NormalizeLegacyCapabilities]: https://github.com/moby/moby/blob/v28.3.2/oci/caps/utils.go#L56 func normalizeCapabilities(caps []string) []string { - var normalized []string - - unique := make(map[string]struct{}) - for _, c := range caps { - c = normalizeCap(c) - if _, ok := unique[c]; ok { - continue - } - unique[c] = struct{}{} - normalized = append(normalized, c) + normalized := slices.Clone(caps) + for i, c := range normalized { + normalized[i] = normalizeCap(c) } - - sort.Strings(normalized) - return normalized + slices.Sort(normalized) + return slices.Compact(normalized) } -// normalizeCap normalizes a capability to its canonical format by upper-casing -// and adding a "CAP_" prefix (if not yet present). It also accepts the "ALL" -// magic-value. -func normalizeCap(capability string) string { - capability = strings.ToUpper(capability) - if capability == allCapabilities { - return capability +// normalizeHostConfig returns a shallow copy of hostConfig with capabilities normalized. +func normalizeHostConfig(hostConfig *container.HostConfig) *container.HostConfig { + if hostConfig == nil { + return nil } - if !strings.HasPrefix(capability, "CAP_") { - capability = "CAP_" + capability - } - return capability + + normalized := *hostConfig + normalized.CapAdd = normalizeCapabilities(hostConfig.CapAdd) + normalized.CapDrop = normalizeCapabilities(hostConfig.CapDrop) + return &normalized } diff --git a/vendor/github.com/moby/moby/client/container_exec.go b/vendor/github.com/moby/moby/client/container_exec.go index 30ed00ea52..86c9474b48 100644 --- a/vendor/github.com/moby/moby/client/container_exec.go +++ b/vendor/github.com/moby/moby/client/container_exec.go @@ -42,7 +42,7 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E return ExecCreateResult{}, err } - req := container.ExecCreateRequest{ + resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, nil, container.ExecCreateRequest{ User: options.User, Privileged: options.Privileged, Tty: options.TTY, @@ -54,9 +54,7 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E Env: options.Env, WorkingDir: options.WorkingDir, Cmd: options.Cmd, - } - - resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, req, nil) + }) defer ensureReaderClosed(resp) if err != nil { return ExecCreateResult{}, err @@ -91,12 +89,11 @@ func (cli *Client) ExecStart(ctx context.Context, execID string, options ExecSta return ExecStartResult{}, err } - req := container.ExecStartRequest{ + resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, nil, container.ExecStartRequest{ Detach: options.Detach, Tty: options.TTY, ConsoleSize: consoleSize, - } - resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, req, nil) + }) defer ensureReaderClosed(resp) return ExecStartResult{}, err } diff --git a/vendor/github.com/moby/moby/client/container_update.go b/vendor/github.com/moby/moby/client/container_update.go index a1d4d249a9..197d8da432 100644 --- a/vendor/github.com/moby/moby/client/container_update.go +++ b/vendor/github.com/moby/moby/client/container_update.go @@ -26,15 +26,10 @@ func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, opti return ContainerUpdateResult{}, err } - updateConfig := container.UpdateConfig{} - if options.Resources != nil { - updateConfig.Resources = *options.Resources - } - if options.RestartPolicy != nil { - updateConfig.RestartPolicy = *options.RestartPolicy - } - - resp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil) + resp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, nil, container.UpdateConfig{ + Resources: valueOrZero(options.Resources), + RestartPolicy: valueOrZero(options.RestartPolicy), + }) defer ensureReaderClosed(resp) if err != nil { return ContainerUpdateResult{}, err diff --git a/vendor/github.com/moby/moby/client/hijack.go b/vendor/github.com/moby/moby/client/hijack.go index 31c44e5988..3b6b12c8fd 100644 --- a/vendor/github.com/moby/moby/client/hijack.go +++ b/vendor/github.com/moby/moby/client/hijack.go @@ -18,7 +18,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu if err != nil { return HijackedResponse{}, err } - req, err := cli.buildRequest(ctx, http.MethodPost, cli.getAPIPath(ctx, path, query), jsonBody, headers) + req, err := cli.buildRequest(ctx, http.MethodPost, cli.getAPIPath(ctx, path, query), headers, jsonBody) if err != nil { return HijackedResponse{}, err } diff --git a/vendor/github.com/moby/moby/client/image_build.go b/vendor/github.com/moby/moby/client/image_build.go index 67ac204aaf..82ddc8ee1a 100644 --- a/vendor/github.com/moby/moby/client/image_build.go +++ b/vendor/github.com/moby/moby/client/image_build.go @@ -32,7 +32,7 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) headers.Set("Content-Type", "application/x-tar") - resp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) + resp, err := cli.postRaw(ctx, "/build", query, headers, buildContext) if err != nil { return ImageBuildResult{}, err } diff --git a/vendor/github.com/moby/moby/client/image_import.go b/vendor/github.com/moby/moby/client/image_import.go index 6c9f22866f..66186f228d 100644 --- a/vendor/github.com/moby/moby/client/image_import.go +++ b/vendor/github.com/moby/moby/client/image_import.go @@ -46,7 +46,7 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re query.Add("changes", change) } - resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil) + resp, err := cli.postRaw(ctx, "/images/create", query, nil, source.Source) if err != nil { return nil, err } diff --git a/vendor/github.com/moby/moby/client/image_load.go b/vendor/github.com/moby/moby/client/image_load.go index ec5fcae6eb..3edd491d22 100644 --- a/vendor/github.com/moby/moby/client/image_load.go +++ b/vendor/github.com/moby/moby/client/image_load.go @@ -42,9 +42,8 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I query["platform"] = p } - resp, err := cli.postRaw(ctx, "/images/load", query, input, http.Header{ - "Content-Type": {"application/x-tar"}, - }) + headers := http.Header{"Content-Type": {"application/x-tar"}} + resp, err := cli.postRaw(ctx, "/images/load", query, headers, input) if err != nil { return nil, err } diff --git a/vendor/github.com/moby/moby/client/image_pull.go b/vendor/github.com/moby/moby/client/image_pull.go index 11c0afa418..04d9edf1bb 100644 --- a/vendor/github.com/moby/moby/client/image_pull.go +++ b/vendor/github.com/moby/moby/client/image_pull.go @@ -89,5 +89,5 @@ func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, resolve hdr.Set(registry.AuthHeader, registryAuth) } } - return cli.post(ctx, "/images/create", query, nil, hdr) + return cli.post(ctx, "/images/create", query, hdr, nil) } diff --git a/vendor/github.com/moby/moby/client/image_push.go b/vendor/github.com/moby/moby/client/image_push.go index 5dd8bc1407..d7729841cc 100644 --- a/vendor/github.com/moby/moby/client/image_push.go +++ b/vendor/github.com/moby/moby/client/image_push.go @@ -94,5 +94,5 @@ func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.V // We use [http.NoBody], which gets marshaled to an empty JSON document. // // see: https://github.com/moby/moby/commit/ea29dffaa541289591aa44fa85d2a596ce860e16 - return cli.post(ctx, "/images/"+imageID+"/push", query, http.NoBody, hdr) + return cli.post(ctx, "/images/"+imageID+"/push", query, hdr, http.NoBody) } diff --git a/vendor/github.com/moby/moby/client/login.go b/vendor/github.com/moby/moby/client/login.go index b295080ab7..9d9e5fad3f 100644 --- a/vendor/github.com/moby/moby/client/login.go +++ b/vendor/github.com/moby/moby/client/login.go @@ -32,7 +32,7 @@ func (cli *Client) RegistryLogin(ctx context.Context, options RegistryLoginOptio RegistryToken: options.RegistryToken, } - resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil) + resp, err := cli.post(ctx, "/auth", url.Values{}, nil, auth) defer ensureReaderClosed(resp) if err != nil { diff --git a/vendor/github.com/moby/moby/client/network_connect.go b/vendor/github.com/moby/moby/client/network_connect.go index 40db955a90..1a5cd645a4 100644 --- a/vendor/github.com/moby/moby/client/network_connect.go +++ b/vendor/github.com/moby/moby/client/network_connect.go @@ -34,7 +34,7 @@ func (cli *Client) NetworkConnect(ctx context.Context, networkID string, options Container: containerID, EndpointConfig: options.EndpointConfig, } - resp, err := cli.post(ctx, "/networks/"+networkID+"/connect", nil, nc, nil) + resp, err := cli.post(ctx, "/networks/"+networkID+"/connect", nil, nil, nc) defer ensureReaderClosed(resp) return NetworkConnectResult{}, err } diff --git a/vendor/github.com/moby/moby/client/network_create.go b/vendor/github.com/moby/moby/client/network_create.go index 25ea32af45..b5dae82b28 100644 --- a/vendor/github.com/moby/moby/client/network_create.go +++ b/vendor/github.com/moby/moby/client/network_create.go @@ -51,7 +51,7 @@ func (cli *Client) NetworkCreate(ctx context.Context, name string, options Netwo req.ConfigFrom = &network.ConfigReference{Network: options.ConfigFrom} } - resp, err := cli.post(ctx, "/networks/create", nil, req, nil) + resp, err := cli.post(ctx, "/networks/create", nil, nil, req) defer ensureReaderClosed(resp) if err != nil { return NetworkCreateResult{}, err diff --git a/vendor/github.com/moby/moby/client/network_disconnect.go b/vendor/github.com/moby/moby/client/network_disconnect.go index 64a1796b8d..4b1a6f550b 100644 --- a/vendor/github.com/moby/moby/client/network_disconnect.go +++ b/vendor/github.com/moby/moby/client/network_disconnect.go @@ -30,11 +30,10 @@ func (cli *Client) NetworkDisconnect(ctx context.Context, networkID string, opti return NetworkDisconnectResult{}, err } - req := network.DisconnectRequest{ + resp, err := cli.post(ctx, "/networks/"+networkID+"/disconnect", nil, nil, network.DisconnectRequest{ Container: containerID, Force: options.Force, - } - resp, err := cli.post(ctx, "/networks/"+networkID+"/disconnect", nil, req, nil) + }) defer ensureReaderClosed(resp) return NetworkDisconnectResult{}, err } diff --git a/vendor/github.com/moby/moby/client/node_update.go b/vendor/github.com/moby/moby/client/node_update.go index 24f87a4df5..cdd80fba12 100644 --- a/vendor/github.com/moby/moby/client/node_update.go +++ b/vendor/github.com/moby/moby/client/node_update.go @@ -25,7 +25,7 @@ func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, options NodeUp query := url.Values{} query.Set("version", options.Version.String()) - resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Spec, nil) + resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, nil, options.Spec) defer ensureReaderClosed(resp) return NodeUpdateResult{}, err } diff --git a/vendor/github.com/moby/moby/client/pkg/jsonmessage/jsonmessage.go b/vendor/github.com/moby/moby/client/pkg/jsonmessage/jsonmessage.go index a955363d8a..7fae6dceaa 100644 --- a/vendor/github.com/moby/moby/client/pkg/jsonmessage/jsonmessage.go +++ b/vendor/github.com/moby/moby/client/pkg/jsonmessage/jsonmessage.go @@ -26,7 +26,8 @@ const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" type DisplayOpt func(*displayOpts) error type displayOpts struct { - auxCallback func(jsonstream.Message) + auxCallback func(jsonstream.Message) + messagePrinter func(jsonstream.Message, io.Writer) error } // WithAuxCallback registers a callback that is invoked for auxiliary @@ -39,6 +40,18 @@ func WithAuxCallback(fn func(jsonstream.Message)) DisplayOpt { } } +// WithMessagePrinter sets a custom printer for non-auxiliary messages. +// If set, [DisplayStream] and [DisplayMessages] pass each message to fn instead +// of using the default terminal-aware presentation. The caller is responsible +// for handling errors reported through [jsonstream.Message.Error] and any +// terminal handling, including progress rendering and terminal-width detection. +func WithMessagePrinter(fn func(jsonstream.Message, io.Writer) error) DisplayOpt { + return func(opts *displayOpts) error { + opts.messagePrinter = fn + return nil + } +} + func RenderTUIProgress(p jsonstream.Progress, width uint16) string { var ( pbBox string @@ -232,13 +245,19 @@ func displayJSONMessages(messages iter.Seq2[jsonstream.Message, error], out io.W } } auxCallback := cfg.auxCallback + messagePrinter := cfg.messagePrinter - ids := make(map[string]uint) - var width uint16 = 200 - if isTerminal { - ws, err := term.GetWinsize(terminalFd) - if err == nil { - width = ws.Width + var ( + ids map[string]uint + width uint16 = 200 + ) + if messagePrinter == nil { + ids = make(map[string]uint) + if isTerminal { + ws, err := term.GetWinsize(terminalFd) + if err == nil { + width = ws.Width + } } } @@ -255,6 +274,13 @@ func displayJSONMessages(messages iter.Seq2[jsonstream.Message, error], out io.W continue } + if messagePrinter != nil { + if err := messagePrinter(jm, out); err != nil { + return err + } + continue + } + if jm.ID != "" && jm.Progress != nil { line, ok := ids[jm.ID] if !ok { diff --git a/vendor/github.com/moby/moby/client/plugin_create.go b/vendor/github.com/moby/moby/client/plugin_create.go index c1a2dd5a6c..f4e7c9a02f 100644 --- a/vendor/github.com/moby/moby/client/plugin_create.go +++ b/vendor/github.com/moby/moby/client/plugin_create.go @@ -25,7 +25,7 @@ func (cli *Client) PluginCreate(ctx context.Context, createContext io.Reader, cr query := url.Values{} query.Set("name", createOptions.RepoName) - resp, err := cli.postRaw(ctx, "/plugins/create", query, createContext, headers) + resp, err := cli.postRaw(ctx, "/plugins/create", query, headers, createContext) defer ensureReaderClosed(resp) return PluginCreateResult{}, err } diff --git a/vendor/github.com/moby/moby/client/plugin_install.go b/vendor/github.com/moby/moby/client/plugin_install.go index a589b2e1fd..d26ee782c3 100644 --- a/vendor/github.com/moby/moby/client/plugin_install.go +++ b/vendor/github.com/moby/moby/client/plugin_install.go @@ -99,13 +99,12 @@ func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, re }) } -func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges plugin.Privileges, registryAuth string) (*http.Response, error) { - return cli.post(ctx, "/plugins/pull", query, privileges, http.Header{ - registry.AuthHeader: {registryAuth}, - }) +func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges []plugin.Privilege, registryAuth string) (*http.Response, error) { + headers := http.Header{registry.AuthHeader: {registryAuth}} + return cli.post(ctx, "/plugins/pull", query, headers, privileges) } -func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options pluginOptions) (plugin.Privileges, error) { +func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options pluginOptions) ([]plugin.Privilege, error) { resp, err := cli.tryPluginPrivileges(ctx, query, options.getRegistryAuth()) if cerrdefs.IsUnauthorized(err) && options.getPrivilegeFunc() != nil { // TODO: do inspect before to check existing name before checking privileges @@ -122,7 +121,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, return nil, err } - var privileges plugin.Privileges + var privileges []plugin.Privilege if err := json.NewDecoder(resp.Body).Decode(&privileges); err != nil { ensureReaderClosed(resp) return nil, err diff --git a/vendor/github.com/moby/moby/client/plugin_push.go b/vendor/github.com/moby/moby/client/plugin_push.go index 4ba25d1336..d206fd2603 100644 --- a/vendor/github.com/moby/moby/client/plugin_push.go +++ b/vendor/github.com/moby/moby/client/plugin_push.go @@ -24,9 +24,8 @@ func (cli *Client) PluginPush(ctx context.Context, name string, options PluginPu if err != nil { return PluginPushResult{}, err } - resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, http.Header{ - registry.AuthHeader: {options.RegistryAuth}, - }) + headers := http.Header{registry.AuthHeader: {options.RegistryAuth}} + resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, headers, nil) if err != nil { return PluginPushResult{}, err } diff --git a/vendor/github.com/moby/moby/client/plugin_set.go b/vendor/github.com/moby/moby/client/plugin_set.go index c1f6bb5fac..309a30c029 100644 --- a/vendor/github.com/moby/moby/client/plugin_set.go +++ b/vendor/github.com/moby/moby/client/plugin_set.go @@ -21,7 +21,7 @@ func (cli *Client) PluginSet(ctx context.Context, name string, options PluginSet return PluginSetResult{}, err } - resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, options.Args, nil) + resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, nil, options.Args) defer ensureReaderClosed(resp) return PluginSetResult{}, err } diff --git a/vendor/github.com/moby/moby/client/plugin_upgrade.go b/vendor/github.com/moby/moby/client/plugin_upgrade.go index f9df6e5843..bcfb1eacfe 100644 --- a/vendor/github.com/moby/moby/client/plugin_upgrade.go +++ b/vendor/github.com/moby/moby/client/plugin_upgrade.go @@ -58,10 +58,9 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options Plugi return resp.Body, nil } -func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges plugin.Privileges, name, registryAuth string) (*http.Response, error) { - return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, http.Header{ - registry.AuthHeader: {registryAuth}, - }) +func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges []plugin.Privilege, name, registryAuth string) (*http.Response, error) { + headers := http.Header{registry.AuthHeader: {registryAuth}} + return cli.post(ctx, "/plugins/"+name+"/upgrade", query, headers, privileges) } func (o *PluginUpgradeOptions) getRegistryAuth() string { diff --git a/vendor/github.com/moby/moby/client/request.go b/vendor/github.com/moby/moby/client/request.go index 10ed36dc67..7d57b6b6ad 100644 --- a/vendor/github.com/moby/moby/client/request.go +++ b/vendor/github.com/moby/moby/client/request.go @@ -19,49 +19,49 @@ import ( // head sends an http request to the docker API using the method HEAD. func (cli *Client) head(ctx context.Context, path string, query url.Values, headers http.Header) (*http.Response, error) { - return cli.sendRequest(ctx, http.MethodHead, path, query, nil, headers) + return cli.sendRequest(ctx, http.MethodHead, path, query, headers, nil) } // get sends an http request to the docker API using the method GET with a specific Go context. func (cli *Client) get(ctx context.Context, path string, query url.Values, headers http.Header) (*http.Response, error) { - return cli.sendRequest(ctx, http.MethodGet, path, query, nil, headers) + return cli.sendRequest(ctx, http.MethodGet, path, query, headers, nil) } // post sends an http POST request to the API. -func (cli *Client) post(ctx context.Context, path string, query url.Values, body any, headers http.Header) (*http.Response, error) { +func (cli *Client) post(ctx context.Context, path string, query url.Values, headers http.Header, body any) (*http.Response, error) { jsonBody, headers, err := prepareJSONRequest(body, headers) if err != nil { return nil, err } - return cli.sendRequest(ctx, http.MethodPost, path, query, jsonBody, headers) + return cli.sendRequest(ctx, http.MethodPost, path, query, headers, jsonBody) } -func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) { - return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers) +func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, headers http.Header, body io.Reader) (*http.Response, error) { + return cli.sendRequest(ctx, http.MethodPost, path, query, headers, body) } -func (cli *Client) put(ctx context.Context, path string, query url.Values, body any, headers http.Header) (*http.Response, error) { +func (cli *Client) put(ctx context.Context, path string, query url.Values, headers http.Header, body any) (*http.Response, error) { jsonBody, headers, err := prepareJSONRequest(body, headers) if err != nil { return nil, err } - return cli.putRaw(ctx, path, query, jsonBody, headers) + return cli.putRaw(ctx, path, query, headers, jsonBody) } // putRaw sends an http request to the docker API using the method PUT. -func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) { +func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, headers http.Header, body io.Reader) (*http.Response, error) { // PUT requests are expected to always have a body (apparently) // so explicitly pass an empty body to sendRequest to signal that // it should set the Content-Type header if not already present. if body == nil { body = http.NoBody } - return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers) + return cli.sendRequest(ctx, http.MethodPut, path, query, headers, body) } // delete sends an http request to the docker API using the method DELETE. func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers http.Header) (*http.Response, error) { - return cli.sendRequest(ctx, http.MethodDelete, path, query, nil, headers) + return cli.sendRequest(ctx, http.MethodDelete, path, query, headers, nil) } // prepareJSONRequest encodes the given body to JSON and returns it as an [io.Reader], and sets the Content-Type @@ -87,7 +87,7 @@ func prepareJSONRequest(body any, headers http.Header) (io.Reader, http.Header, return jsonBody, hdr, nil } -func (cli *Client) buildRequest(ctx context.Context, method, path string, body io.Reader, headers http.Header) (*http.Request, error) { +func (cli *Client) buildRequest(ctx context.Context, method, path string, headers http.Header, body io.Reader) (*http.Request, error) { req, err := http.NewRequestWithContext(ctx, method, path, body) if err != nil { return nil, err @@ -104,8 +104,8 @@ func (cli *Client) buildRequest(ctx context.Context, method, path string, body i return req, nil } -func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) { - req, err := cli.buildRequest(ctx, method, cli.getAPIPath(ctx, path, query), body, headers) +func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, headers http.Header, body io.Reader) (*http.Response, error) { + req, err := cli.buildRequest(ctx, method, cli.getAPIPath(ctx, path, query), headers, body) if err != nil { return nil, err } diff --git a/vendor/github.com/moby/moby/client/secret_create.go b/vendor/github.com/moby/moby/client/secret_create.go index 8e59a42ce7..22d5362d7f 100644 --- a/vendor/github.com/moby/moby/client/secret_create.go +++ b/vendor/github.com/moby/moby/client/secret_create.go @@ -19,7 +19,7 @@ type SecretCreateResult struct { // SecretCreate creates a new secret. func (cli *Client) SecretCreate(ctx context.Context, options SecretCreateOptions) (SecretCreateResult, error) { - resp, err := cli.post(ctx, "/secrets/create", nil, options.Spec, nil) + resp, err := cli.post(ctx, "/secrets/create", nil, nil, options.Spec) defer ensureReaderClosed(resp) if err != nil { return SecretCreateResult{}, err diff --git a/vendor/github.com/moby/moby/client/secret_update.go b/vendor/github.com/moby/moby/client/secret_update.go index d50fba4d45..b9fe94c71a 100644 --- a/vendor/github.com/moby/moby/client/secret_update.go +++ b/vendor/github.com/moby/moby/client/secret_update.go @@ -24,7 +24,7 @@ func (cli *Client) SecretUpdate(ctx context.Context, id string, options SecretUp } query := url.Values{} query.Set("version", options.Version.String()) - resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, options.Spec, nil) + resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, nil, options.Spec) defer ensureReaderClosed(resp) if err != nil { return SecretUpdateResult{}, err diff --git a/vendor/github.com/moby/moby/client/service_create.go b/vendor/github.com/moby/moby/client/service_create.go index 8d5926bb9b..e911e5783d 100644 --- a/vendor/github.com/moby/moby/client/service_create.go +++ b/vendor/github.com/moby/moby/client/service_create.go @@ -78,7 +78,7 @@ func (cli *Client) ServiceCreate(ctx context.Context, options ServiceCreateOptio if options.EncodedRegistryAuth != "" { headers[registry.AuthHeader] = []string{options.EncodedRegistryAuth} } - resp, err := cli.post(ctx, "/services/create", nil, options.Spec, headers) + resp, err := cli.post(ctx, "/services/create", nil, headers, options.Spec) defer ensureReaderClosed(resp) if err != nil { return ServiceCreateResult{}, err diff --git a/vendor/github.com/moby/moby/client/service_update.go b/vendor/github.com/moby/moby/client/service_update.go index 2505fe4b8e..4f179f0a7d 100644 --- a/vendor/github.com/moby/moby/client/service_update.go +++ b/vendor/github.com/moby/moby/client/service_update.go @@ -101,7 +101,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, options if options.EncodedRegistryAuth != "" { headers.Set(registry.AuthHeader, options.EncodedRegistryAuth) } - resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, options.Spec, headers) + resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, headers, options.Spec) defer ensureReaderClosed(resp) if err != nil { return ServiceUpdateResult{}, err diff --git a/vendor/github.com/moby/moby/client/swarm_init.go b/vendor/github.com/moby/moby/client/swarm_init.go index caad560856..87be2ce10f 100644 --- a/vendor/github.com/moby/moby/client/swarm_init.go +++ b/vendor/github.com/moby/moby/client/swarm_init.go @@ -29,7 +29,7 @@ type SwarmInitResult struct { // SwarmInit initializes the swarm. func (cli *Client) SwarmInit(ctx context.Context, options SwarmInitOptions) (SwarmInitResult, error) { - req := swarm.InitRequest{ + resp, err := cli.post(ctx, "/swarm/init", nil, nil, swarm.InitRequest{ ListenAddr: options.ListenAddr, AdvertiseAddr: options.AdvertiseAddr, DataPathAddr: options.DataPathAddr, @@ -40,9 +40,7 @@ func (cli *Client) SwarmInit(ctx context.Context, options SwarmInitOptions) (Swa Availability: options.Availability, DefaultAddrPool: options.DefaultAddrPool, SubnetSize: options.SubnetSize, - } - - resp, err := cli.post(ctx, "/swarm/init", nil, req, nil) + }) defer ensureReaderClosed(resp) if err != nil { return SwarmInitResult{}, err diff --git a/vendor/github.com/moby/moby/client/swarm_join.go b/vendor/github.com/moby/moby/client/swarm_join.go index 66a7544821..9d33ef6e12 100644 --- a/vendor/github.com/moby/moby/client/swarm_join.go +++ b/vendor/github.com/moby/moby/client/swarm_join.go @@ -23,16 +23,14 @@ type SwarmJoinResult struct { // SwarmJoin joins the swarm. func (cli *Client) SwarmJoin(ctx context.Context, options SwarmJoinOptions) (SwarmJoinResult, error) { - req := swarm.JoinRequest{ + resp, err := cli.post(ctx, "/swarm/join", nil, nil, swarm.JoinRequest{ ListenAddr: options.ListenAddr, AdvertiseAddr: options.AdvertiseAddr, DataPathAddr: options.DataPathAddr, RemoteAddrs: options.RemoteAddrs, JoinToken: options.JoinToken, Availability: options.Availability, - } - - resp, err := cli.post(ctx, "/swarm/join", nil, req, nil) + }) defer ensureReaderClosed(resp) return SwarmJoinResult{}, err } diff --git a/vendor/github.com/moby/moby/client/swarm_unlock.go b/vendor/github.com/moby/moby/client/swarm_unlock.go index 92335afb54..a6aee22986 100644 --- a/vendor/github.com/moby/moby/client/swarm_unlock.go +++ b/vendor/github.com/moby/moby/client/swarm_unlock.go @@ -16,10 +16,9 @@ type SwarmUnlockResult struct{} // SwarmUnlock unlocks locked swarm. func (cli *Client) SwarmUnlock(ctx context.Context, options SwarmUnlockOptions) (SwarmUnlockResult, error) { - req := &swarm.UnlockRequest{ + resp, err := cli.post(ctx, "/swarm/unlock", nil, nil, swarm.UnlockRequest{ UnlockKey: options.Key, - } - resp, err := cli.post(ctx, "/swarm/unlock", nil, req, nil) + }) defer ensureReaderClosed(resp) return SwarmUnlockResult{}, err } diff --git a/vendor/github.com/moby/moby/client/swarm_update.go b/vendor/github.com/moby/moby/client/swarm_update.go index 81f62b2c02..0e6257ad46 100644 --- a/vendor/github.com/moby/moby/client/swarm_update.go +++ b/vendor/github.com/moby/moby/client/swarm_update.go @@ -27,7 +27,7 @@ func (cli *Client) SwarmUpdate(ctx context.Context, options SwarmUpdateOptions) query.Set("rotateWorkerToken", strconv.FormatBool(options.RotateWorkerToken)) query.Set("rotateManagerToken", strconv.FormatBool(options.RotateManagerToken)) query.Set("rotateManagerUnlockKey", strconv.FormatBool(options.RotateManagerUnlockKey)) - resp, err := cli.post(ctx, "/swarm/update", query, options.Spec, nil) + resp, err := cli.post(ctx, "/swarm/update", query, nil, options.Spec) defer ensureReaderClosed(resp) return SwarmUpdateResult{}, err } diff --git a/vendor/github.com/moby/moby/client/utils.go b/vendor/github.com/moby/moby/client/utils.go index 1c0d09dfa2..ceecafa55c 100644 --- a/vendor/github.com/moby/moby/client/utils.go +++ b/vendor/github.com/moby/moby/client/utils.go @@ -33,6 +33,14 @@ func trimID(objType, id string) (string, error) { return id, nil } +// valueOrZero returns the value pointed to by p, or the zero value of T if p is nil. +func valueOrZero[T any](p *T) (zero T) { + if p != nil { + return *p + } + return zero +} + // parseAPIVersion checks v to be a well-formed (".") // API version. It returns an error if the value is empty or does not // have the correct format, but does not validate if the API version is diff --git a/vendor/github.com/moby/moby/client/volume_create.go b/vendor/github.com/moby/moby/client/volume_create.go index 674e063357..5212f09a5f 100644 --- a/vendor/github.com/moby/moby/client/volume_create.go +++ b/vendor/github.com/moby/moby/client/volume_create.go @@ -23,14 +23,13 @@ type VolumeCreateResult struct { // VolumeCreate creates a volume in the docker host. func (cli *Client) VolumeCreate(ctx context.Context, options VolumeCreateOptions) (VolumeCreateResult, error) { - createRequest := volume.CreateRequest{ + resp, err := cli.post(ctx, "/volumes/create", nil, nil, volume.CreateRequest{ Name: options.Name, Driver: options.Driver, DriverOpts: options.DriverOpts, Labels: options.Labels, ClusterVolumeSpec: options.ClusterVolumeSpec, - } - resp, err := cli.post(ctx, "/volumes/create", nil, createRequest, nil) + }) defer ensureReaderClosed(resp) if err != nil { return VolumeCreateResult{}, err diff --git a/vendor/github.com/moby/moby/client/volume_update.go b/vendor/github.com/moby/moby/client/volume_update.go index 5aa2a0aa17..3810267ef5 100644 --- a/vendor/github.com/moby/moby/client/volume_update.go +++ b/vendor/github.com/moby/moby/client/volume_update.go @@ -31,7 +31,7 @@ func (cli *Client) VolumeUpdate(ctx context.Context, volumeID string, options Vo query := url.Values{} query.Set("version", options.Version.String()) - resp, err := cli.put(ctx, "/volumes/"+volumeID, query, options, nil) + resp, err := cli.put(ctx, "/volumes/"+volumeID, query, nil, options) defer ensureReaderClosed(resp) if err != nil { return VolumeUpdateResult{}, err diff --git a/vendor/modules.txt b/vendor/modules.txt index a5a6f1918b..b4d393ebf4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -190,7 +190,7 @@ github.com/moby/moby/api/types/storage github.com/moby/moby/api/types/swarm github.com/moby/moby/api/types/system github.com/moby/moby/api/types/volume -# github.com/moby/moby/client v0.5.1 +# github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 ## explicit; go 1.24 github.com/moby/moby/client github.com/moby/moby/client/internal From 09d30a34bf8c7b1fe9c323b23935a1ddb7af8ec0 Mon Sep 17 00:00:00 2001 From: Youfu Zhang Date: Wed, 2 Sep 2026 10:49:36 +0800 Subject: [PATCH 38/44] cli/command/container: add create/run --umask Signed-off-by: Youfu Zhang --- cli/command/container/opts.go | 5 ++ cli/command/container/opts_test.go | 13 ++++ .../reference/commandline/container_create.md | 1 + docs/reference/commandline/container_run.md | 51 +++++++++++++++ docs/reference/commandline/create.md | 1 + docs/reference/commandline/run.md | 1 + e2e/container/run_test.go | 25 ++++++++ opts/opts.go | 37 +++++++++++ opts/opts_test.go | 63 +++++++++++++++++++ 9 files changed, 197 insertions(+) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 680a84a8b5..94b8dffa34 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -139,6 +139,7 @@ type containerOptions struct { runtime string autoRemove bool init bool + umask opts.UmaskOpt annotations *opts.MapOpts Image string @@ -313,6 +314,9 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm") flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use") flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container") + flags.Var(&copts.umask, "umask", "Set umask for the container") + flags.SetAnnotation("umask", "version", []string{"1.56"}) + flags.SetAnnotation("umask", "ostype", []string{"linux"}) flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes") flags.SetAnnotation("init", "version", []string{"1.25"}) @@ -710,6 +714,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con MaskedPaths: maskedPaths, ReadonlyPaths: readonlyPaths, Annotations: copts.annotations.GetAll(), + Umask: copts.umask.Value(), } if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() { diff --git a/cli/command/container/opts_test.go b/cli/command/container/opts_test.go index 0781e99687..556e013066 100644 --- a/cli/command/container/opts_test.go +++ b/cli/command/container/opts_test.go @@ -154,6 +154,19 @@ func TestParseRunLinks(t *testing.T) { } } +func TestParseRunWithoutUmask(t *testing.T) { + _, hostConfig, _, err := parseRun([]string{"ubuntu", "bash"}) + assert.NilError(t, err) + assert.Assert(t, hostConfig.Umask == nil) +} + +func TestParseRunUmask(t *testing.T) { + _, hostConfig, _, err := parseRun([]string{"--umask", "0022", "ubuntu", "bash"}) + assert.NilError(t, err) + assert.Assert(t, hostConfig.Umask != nil) + assert.Equal(t, uint32(0o22), *hostConfig.Umask) +} + func TestParseRunAttach(t *testing.T) { tests := []struct { input string diff --git a/docs/reference/commandline/container_create.md b/docs/reference/commandline/container_create.md index d69b163389..a2ee6147a5 100644 --- a/docs/reference/commandline/container_create.md +++ b/docs/reference/commandline/container_create.md @@ -102,6 +102,7 @@ Create a new container | `--tmpfs` | `list` | | Mount a tmpfs directory | | `-t`, `--tty` | `bool` | | Allocate a pseudo-TTY | | `--ulimit` | `ulimit` | | Ulimit options | +| `--umask` | `umask` | `` | Set umask for the container | | `--use-api-socket` | `bool` | | Bind mount Docker API socket and required auth | | `-u`, `--user` | `string` | | Username or UID (format: [:]) | | `--userns` | `string` | | User namespace to use | diff --git a/docs/reference/commandline/container_run.md b/docs/reference/commandline/container_run.md index 1dcc0fd538..79d04c9c0a 100644 --- a/docs/reference/commandline/container_run.md +++ b/docs/reference/commandline/container_run.md @@ -105,6 +105,7 @@ Create and run a new container from an image | [`--tmpfs`](#tmpfs) | `list` | | Mount a tmpfs directory | | [`-t`](#tty), [`--tty`](#tty) | `bool` | | Allocate a pseudo-TTY | | [`--ulimit`](#ulimit) | `ulimit` | | Ulimit options | +| [`--umask`](#umask) | `umask` | `` | Set umask for the container | | `--use-api-socket` | `bool` | | Bind mount Docker API socket and required auth | | `-u`, `--user` | `string` | | Username or UID (format: [:]) | | [`--userns`](#userns) | `string` | | User namespace to use | @@ -1392,6 +1393,56 @@ The 4th container fails and reports a "[8] System error: resource temporarily un This fails because the caller set `nproc=3` resulting in the first three containers using up the three processes quota set for the `daemon` user. +### Set umask for the container (--umask) + +The `--umask` flag sets the umask for the container's processes, which +controls the default permissions for files and directories created inside +the container. The value must be specified in octal notation. Leading zeros +are optional. For example, a umask of `022` creates new files with `644` +permissions (`-rw-r--r--`) and new directories with `755` permissions +(`drwxr-xr-x`). + +If you don't set the `--umask` flag, the OCI runtime applies its own +default umask. The default OCI runtime (runc) uses a default umask of +`0022`, but this value is implementation-defined and may vary between OCI +runtimes: + +```console +$ docker run --rm busybox sh -c umask +0022 +``` + +When the `--umask` flag is set, the value is included in the OCI process +configuration used for the container's entrypoint, for processes started +with `docker exec`, and for healthchecks. Whether an OCI runtime honors the +value depends on the runtime; runc honors the configured umask for +`docker exec` and for healthchecks. + +To set a custom umask, use the `--umask` flag with an octal value: + +```console +$ docker run --rm --umask 077 busybox sh -c umask +0077 +``` + +The umask is also applied to processes started later with `docker exec`: + +```console +$ docker run --rm -d --name umask-test --umask 077 busybox sleep 60 +$ docker exec umask-test sh -c umask +0077 +``` + +Setting the umask to `0` masks no permission bits, so files and directories +keep their full permissions. The following example creates a file and a +directory inside the container: + +```console +$ docker run --rm --umask 0 busybox sh -c 'touch file && mkdir dir && stat -c "%a %n" file dir' +666 file +777 dir +``` + ### Stop container with signal (--stop-signal) The `--stop-signal` flag sends the system call signal to the diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index 2a46bec81a..e5699b8eb0 100644 --- a/docs/reference/commandline/create.md +++ b/docs/reference/commandline/create.md @@ -102,6 +102,7 @@ Create a new container | `--tmpfs` | `list` | | Mount a tmpfs directory | | `-t`, `--tty` | `bool` | | Allocate a pseudo-TTY | | `--ulimit` | `ulimit` | | Ulimit options | +| `--umask` | `umask` | `` | Set umask for the container | | `--use-api-socket` | `bool` | | Bind mount Docker API socket and required auth | | `-u`, `--user` | `string` | | Username or UID (format: [:]) | | `--userns` | `string` | | User namespace to use | diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index d544a9cdf5..b114301def 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -105,6 +105,7 @@ Create and run a new container from an image | `--tmpfs` | `list` | | Mount a tmpfs directory | | `-t`, `--tty` | `bool` | | Allocate a pseudo-TTY | | `--ulimit` | `ulimit` | | Ulimit options | +| `--umask` | `umask` | `` | Set umask for the container | | `--use-api-socket` | `bool` | | Bind mount Docker API socket and required auth | | `-u`, `--user` | `string` | | Username or UID (format: [:]) | | `--userns` | `string` | | User namespace to use | diff --git a/e2e/container/run_test.go b/e2e/container/run_test.go index 574a203603..9151131a47 100644 --- a/e2e/container/run_test.go +++ b/e2e/container/run_test.go @@ -110,6 +110,31 @@ func TestRunWithCgroupNamespace(t *testing.T) { result.Assert(t, icmd.Success) } +func TestRunUmask(t *testing.T) { + environment.SkipIfDaemonNotLinux(t) + + testCases := []struct { + name string + args []string + expected string + }{ + {name: "unset", expected: "0022\n"}, + {name: "zero", args: []string{"--umask", "0"}, expected: "0000\n"}, + {name: "octal-022", args: []string{"--umask", "022"}, expected: "0022\n"}, + {name: "octal-777", args: []string{"--umask", "777"}, expected: "0777\n"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + args := []string{"run", "--rm", fixtures.AlpineImage, "sh", "-c", "umask"} + args = append(args[:2], append(tc.args, args[2:]...)...) + result := icmd.RunCommand("docker", args...) + result.Assert(t, icmd.Success) + assert.Equal(t, result.Stdout(), tc.expected) + }) + } +} + func TestMountSubvolume(t *testing.T) { skip.If(t, versions.LessThan(environment.DaemonAPIVersion(t), "1.45")) volName := "test-volume-" + t.Name() diff --git a/opts/opts.go b/opts/opts.go index b944a479d8..0a7706ca71 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -11,6 +11,7 @@ import ( "net" "path" "slices" + "strconv" "strings" "github.com/docker/cli/internal/lazyregexp" @@ -477,3 +478,39 @@ func (m *MemSwapBytes) UnmarshalJSON(s []byte) error { b := MemBytes(*m) return b.UnmarshalJSON(s) } + +// UmaskOpt is a type for umask values in octal format +type UmaskOpt struct { + ptr *uint32 +} + +// Set sets the value of the UmaskOpt by passing a string in octal format +func (u *UmaskOpt) Set(s string) error { + v, err := strconv.ParseUint(s, 8, 32) + if err != nil { + return err + } + if u.ptr == nil { + u.ptr = new(uint32) + } + *u.ptr = uint32(v) + return nil +} + +// Type returns the type +func (*UmaskOpt) Type() string { + return "umask" +} + +// Value returns the uint32 ptr +func (u *UmaskOpt) Value() *uint32 { + return u.ptr +} + +// String returns the umask value in octal format, or "" if the pointer is nil. +func (u *UmaskOpt) String() string { + if u.ptr == nil { + return "" + } + return fmt.Sprintf("%#04o", uint64(*u.ptr)) +} diff --git a/opts/opts_test.go b/opts/opts_test.go index e978c2ea87..598a54a90e 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -428,3 +428,66 @@ func TestParseCPUsReturnZeroOnInvalidValues(t *testing.T) { resValue, _ = ParseCPUs("1e-32") assert.Equal(t, z1, resValue) } + +func TestUmaskOpt(t *testing.T) { + t.Run("type", func(t *testing.T) { + var opt UmaskOpt + assert.Equal(t, opt.Type(), "umask") + }) + + t.Run("nil by default", func(t *testing.T) { + var opt UmaskOpt + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "") + }) + + t.Run("rejects empty string", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("") + assert.Assert(t, err != nil) + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "") + }) + + t.Run("parses zero", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("0") + assert.NilError(t, err) + assert.Equal(t, *opt.Value(), uint32(0)) + assert.Equal(t, opt.String(), "0000") + }) + + t.Run("parses valid octal values", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("022") + assert.NilError(t, err) + assert.Assert(t, opt.Value() != nil) + assert.Equal(t, *opt.Value(), uint32(0o22)) + assert.Equal(t, opt.String(), "0022") + }) + + t.Run("rejects octal values with 0o prefix", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("0o22") + assert.Assert(t, err != nil) + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "") + }) + + t.Run("parses large octal values", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("077777") + assert.NilError(t, err) + assert.Assert(t, opt.Value() != nil) + assert.Equal(t, *opt.Value(), uint32(0o77777)) + assert.Equal(t, opt.String(), "077777") + }) + + t.Run("rejects invalid octal values", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("9") + assert.Assert(t, err != nil) + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "") + }) +} From d7036970e853489de0f81c0b26429833d178eef6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 3 Sep 2026 21:49:09 +0200 Subject: [PATCH 39/44] cmd/docker-trust: fix tests Some test-files were missing. Signed-off-by: Sebastiaan van Stijn --- cmd/docker-trust/internal/test/cmd.go | 81 ++++++++++++++++++++++++ cmd/docker-trust/internal/test/writer.go | 25 ++++++++ cmd/docker-trust/trust/inspect.go | 2 +- 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 cmd/docker-trust/internal/test/cmd.go create mode 100644 cmd/docker-trust/internal/test/writer.go diff --git a/cmd/docker-trust/internal/test/cmd.go b/cmd/docker-trust/internal/test/cmd.go new file mode 100644 index 0000000000..8b98d2df62 --- /dev/null +++ b/cmd/docker-trust/internal/test/cmd.go @@ -0,0 +1,81 @@ +package test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/docker/cli/cli/streams" + "github.com/docker/cli/internal/prompt" + "github.com/spf13/cobra" + "gotest.tools/v3/assert" +) + +func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli *FakeCli) { + t.Helper() + + errChan := make(chan error) + defer close(errChan) + + // wrap the out stream to detect when the prompt is ready + writerHookChan := make(chan struct{}) + defer close(writerHookChan) + + outStream := streams.NewOut(NewWriterWithHook(cli.OutBuffer(), func(p []byte) { + writerHookChan <- struct{}{} + })) + cli.SetOut(outStream) + + r, _, err := os.Pipe() + assert.NilError(t, err) + cli.SetIn(streams.NewIn(r)) + + notifyCtx, notifyCancel := context.WithCancel(ctx) + t.Cleanup(notifyCancel) + + go func() { + errChan <- cmd.ExecuteContext(notifyCtx) + }() + + writeCtx, writeCancel := context.WithTimeout(ctx, 100*time.Millisecond) + defer writeCancel() + + // wait for the prompt to be ready + select { + case <-writeCtx.Done(): + t.Fatalf("command %s did not write prompt to stdout", cmd.Name()) + case <-writerHookChan: + // drain the channel for future buffer writes + go func() { + for { + select { + case <-ctx.Done(): + return + case <-writerHookChan: + } + } + }() + } + + assert.Check(t, cli.OutBuffer().Len() > 0) + + // a small delay to ensure the plugin is prompting + time.Sleep(100 * time.Microsecond) + + errCtx, errCancel := context.WithTimeout(ctx, 100*time.Millisecond) + defer errCancel() + + // sigint and sigterm are caught by the prompt + // this allows us to gracefully exit the prompt with a 0 exit code + notifyCancel() + + select { + case <-errCtx.Done(): + t.Logf("command stdout:\n%s\n", cli.OutBuffer().String()) + t.Logf("command stderr:\n%s\n", cli.ErrBuffer().String()) + t.Fatalf("command %s did not return after SIGINT", cmd.Name()) + case err := <-errChan: + assert.ErrorIs(t, err, prompt.ErrTerminated) + } +} diff --git a/cmd/docker-trust/internal/test/writer.go b/cmd/docker-trust/internal/test/writer.go new file mode 100644 index 0000000000..3d922fb57a --- /dev/null +++ b/cmd/docker-trust/internal/test/writer.go @@ -0,0 +1,25 @@ +package test + +import ( + "io" +) + +type writerWithHook struct { + actualWriter io.Writer + hook func([]byte) +} + +func (w *writerWithHook) Write(p []byte) (n int, err error) { + defer w.hook(p) + return w.actualWriter.Write(p) +} + +var _ io.Writer = (*writerWithHook)(nil) + +// NewWriterWithHook returns a io.Writer that still +// writes to the actualWriter but also calls the hook function +// after every write. It is useful to use this function when +// you need to wait for a writer to complete writing inside a test. +func NewWriterWithHook(actualWriter io.Writer, hook func([]byte)) *writerWithHook { + return &writerWithHook{actualWriter: actualWriter, hook: hook} +} diff --git a/cmd/docker-trust/trust/inspect.go b/cmd/docker-trust/trust/inspect.go index 74240b61ad..fe16121ce7 100644 --- a/cmd/docker-trust/trust/inspect.go +++ b/cmd/docker-trust/trust/inspect.go @@ -81,7 +81,7 @@ func getRepoTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) } } - var signerList []trustSigner + signerList := []trustSigner{} for signerName, signerKeys := range getDelegationRoleToKeyMap(delegationRoles) { signerKeyList := make([]trustKey, 0, len(signerKeys)) for _, keyID := range signerKeys { From 32bfe541489eb4152a7e6cbd08f39498cff0d54a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 3 Sep 2026 21:37:49 +0200 Subject: [PATCH 40/44] cmd/docker-trust: update dependencies Signed-off-by: Sebastiaan van Stijn --- cmd/docker-trust/go.mod | 75 +++++++------- cmd/docker-trust/go.sum | 208 ++++++++++++++++----------------------- cmd/docker-trust/main.go | 3 +- 3 files changed, 123 insertions(+), 163 deletions(-) diff --git a/cmd/docker-trust/go.mod b/cmd/docker-trust/go.mod index 10d14f7f84..edbd6f2a25 100644 --- a/cmd/docker-trust/go.mod +++ b/cmd/docker-trust/go.mod @@ -1,23 +1,23 @@ module github.com/docker/cli/cmd/docker-trust -go 1.25.0 +go 1.26.0 require ( github.com/containerd/errdefs v1.0.0 github.com/distribution/reference v0.6.0 - github.com/docker/cli v29.4.0+incompatible + github.com/docker/cli v29.7.2+incompatible github.com/docker/cli-docs-tool v0.11.0 github.com/docker/distribution v2.8.3+incompatible - github.com/docker/go-connections v0.7.0 + github.com/docker/go-connections v0.8.1 github.com/fvbommel/sortorder v1.2.0 - github.com/moby/moby/api v1.54.2 - github.com/moby/moby/client v0.4.1 + github.com/moby/moby/api v1.55.0 + github.com/moby/moby/client v0.5.1 github.com/opencontainers/go-digest v1.0.0 - github.com/sirupsen/logrus v1.9.4 + github.com/sirupsen/logrus v1.10.2 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a - go.opentelemetry.io/otel v1.43.0 + go.opentelemetry.io/otel v1.46.0 gotest.tools/v3 v3.5.2 ) @@ -30,48 +30,49 @@ require ( github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v1.0.0-rc.2 // indirect - github.com/docker/docker-credential-helpers v0.9.5 // indirect + github.com/containerd/platforms v1.0.0-rc.5 // indirect + github.com/docker/docker-credential-helpers v0.9.9 // indirect github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect - github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-metrics v0.1.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/felixge/httpsnoop v1.1.0 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/mattn/go-runewidth v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.28 // indirect github.com/miekg/pkcs11 v1.1.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/sequential v0.7.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/morikuni/aec v1.1.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect - golang.org/x/crypto v0.52.0 // indirect - golang.org/x/net v0.54.0 // indirect - golang.org/x/sys v0.45.0 // indirect - golang.org/x/term v0.43.0 // indirect - golang.org/x/text v0.37.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/grpc v1.80.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 // indirect + go.opentelemetry.io/otel/metric v1.46.0 // indirect + go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect + go.opentelemetry.io/otel/trace v1.46.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.0 // indirect + golang.org/x/crypto v0.56.0 // indirect + golang.org/x/net v0.58.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/term v0.45.0 // indirect + golang.org/x/text v0.41.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect + google.golang.org/grpc v1.83.2 // indirect + google.golang.org/protobuf v1.36.12 // indirect ) diff --git a/cmd/docker-trust/go.sum b/cmd/docker-trust/go.sum index e4ab52452e..a92361e9a3 100644 --- a/cmd/docker-trust/go.sum +++ b/cmd/docker-trust/go.sum @@ -6,10 +6,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ= github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= @@ -35,81 +31,71 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= -github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/platforms v1.0.0-rc.5 h1:vXd569rDrz8LeMXzAnBsy6LADV5YtsD8oyaRarxdmSU= +github.com/containerd/platforms v1.0.0-rc.5/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM= -github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.7.2+incompatible h1:dlkwallR8XqfeVnA2ELEhdwvb4lsSwuB4IgsG8Q9cLY= +github.com/docker/cli v29.7.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= -github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/docker-credential-helpers v0.9.9 h1:BkydjIgZ46JnDbqyM2p2fc63KMw6y+KHL3Em/2AGJ7w= +github.com/docker/docker-credential-helpers v0.9.9/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= -github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= +github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M= +github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-metrics v0.1.0 h1:r76KPNpstz+IvQKSWpYegSkkyzex0V3A1ZGVx6bhGlY= +github.com/docker/go-metrics v0.1.0/go.mod h1:PciI3sONtB051kXALN1JoIlpcu54E1FuPh+4DuqEzyw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= +github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fvbommel/sortorder v1.2.0 h1:TRIiRiGX+djh3Yf4FVxmWmAcYfIr5dH0NbzJWOSAWZk= github.com/fvbommel/sortorder v1.2.0/go.mod h1:LbhO04ijZIeUuvz9B9BkI/qYrpZZEn1gWhxv4QjUKVs= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 h1:jc2UWq7CbdszqeH6qu1ougXMIUBfSy8Pbh/anURYbGI= github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -121,12 +107,9 @@ github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8c github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc= github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -134,11 +117,13 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y= github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= -github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-runewidth v0.0.28 h1:rPyg2ybwEKPebvpzVWe1gKBkH8EQFkxO4Y0hjBeLaBU= +github.com/mattn/go-runewidth v0.0.28/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -148,23 +133,20 @@ github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KH github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= -github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= -github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc= +github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw= +github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8= +github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= @@ -177,40 +159,27 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk= github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -225,88 +194,82 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg= github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ7tGUHvcvL1v3yR6NcCc9nOqh2L+CG6HWrYQtwzQ0= github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= +go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= +go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0 h1:qkDYCAFiZXLcs1L4aY+tP2wguQ4kURANqHOQMA2et2s= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0/go.mod h1:tkipS4DRzmpAmvg+Gw4++O1IdDq6TVDnvnYU6cmbQVs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 h1:OFnwLJr+pF3iHrlGSzbxyuo6/6HyBlnlN1CWEJmBVcw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0/go.mod h1:716wFneO0ov19A2beH5hjfh9AK5z/VWNAtDijp1Y0/g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 h1:w53CDeOA/Kurp7yRsegSr6pbbr759dOvJ+yNmWM6Hxs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0/go.mod h1:BOmGMCbAtvcJiSJ+hLuhgPLdDbimnraSl8irz3iY8sY= +go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= +go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= +go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU= +go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4= +go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= +go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= +go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= +go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= +go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= +go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= +golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 h1:izFU9hz7aeLI/Mi1J0991ae+xcwRLr7hTqWnB/9aIIU= +google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5/go.mod h1:3LhxRw4YYkf+ylAfgaY9JlVLFKhokkCV8duhLLe7+t0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= +google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -316,14 +279,11 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= diff --git a/cmd/docker-trust/main.go b/cmd/docker-trust/main.go index b4afbea5f1..288b40eeb1 100644 --- a/cmd/docker-trust/main.go +++ b/cmd/docker-trust/main.go @@ -95,8 +95,7 @@ func getExitCode(err error) int { return 0 } - var userTerminatedErr errCtxSignalTerminated - if errors.As(err, &userTerminatedErr) { + if userTerminatedErr, ok := errors.AsType[errCtxSignalTerminated](err); ok { s, ok := userTerminatedErr.signal.(syscall.Signal) if !ok { return 1 From 45a3e0bc2aece19bb2970905231b97dc3a86568b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 3 Sep 2026 22:12:34 +0200 Subject: [PATCH 41/44] e2e/container: Skip umask test until engine support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- e2e/container/run_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/container/run_test.go b/e2e/container/run_test.go index 9151131a47..d45fe0865d 100644 --- a/e2e/container/run_test.go +++ b/e2e/container/run_test.go @@ -112,6 +112,7 @@ func TestRunWithCgroupNamespace(t *testing.T) { func TestRunUmask(t *testing.T) { environment.SkipIfDaemonNotLinux(t) + t.Skip("FIXME: un-skip once e2e tests v29.8.0") testCases := []struct { name string From 7cf09ba4856b903cf7ec9603674e8a63f16d9fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 3 Sep 2026 23:00:14 +0200 Subject: [PATCH 42/44] vendor: github.com/moby/moby/api v1.56.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit full diff: https://github.com/moby/moby/compare/b9b109e4d341...api/v1.56.0 Signed-off-by: Paweł Gronowski --- vendor.mod | 2 +- vendor.sum | 4 ++-- vendor/modules.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor.mod b/vendor.mod index 9a51eb9201..1f61fa1659 100644 --- a/vendor.mod +++ b/vendor.mod @@ -33,7 +33,7 @@ require ( github.com/google/uuid v1.6.0 github.com/mattn/go-runewidth v0.0.28 github.com/moby/go-archive v0.3.3 - github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 + github.com/moby/moby/api v1.56.0 github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 github.com/moby/patternmatcher v0.6.1 github.com/moby/swarmkit/v2 v2.1.2 diff --git a/vendor.sum b/vendor.sum index 91237332b9..12d4f04641 100644 --- a/vendor.sum +++ b/vendor.sum @@ -88,8 +88,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME= github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= -github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 h1:ooFZgSrQ/xRDfGves7imF3goa+32bEcJrcXIDlL/qpI= -github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/api v1.56.0 h1:GQzua3NA599ASSIICx0iFgiJeO9YkdDARvQsm23ZZuQ= +github.com/moby/moby/api v1.56.0/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk= github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 h1:/XUuyJxH/HXe5MBcQo+YG9hAtZTQNpWFeJ5PcxL+hs8= github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= diff --git a/vendor/modules.txt b/vendor/modules.txt index b4d393ebf4..ef41fdffa8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -168,7 +168,7 @@ github.com/moby/go-archive github.com/moby/go-archive/compression github.com/moby/go-archive/internal/archiveoptions github.com/moby/go-archive/tarheader -# github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 +# github.com/moby/moby/api v1.56.0 ## explicit; go 1.24 github.com/moby/moby/api/pkg/authconfig github.com/moby/moby/api/pkg/stdcopy From d5ea3bdc8ae2742ec1dc35a477a89d8595fb5a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 3 Sep 2026 23:01:04 +0200 Subject: [PATCH 43/44] vendor: github.com/moby/moby/client v0.6.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit full diff: https://github.com/moby/moby/compare/b9b109e4d341...client/v0.6.0 Signed-off-by: Paweł Gronowski --- vendor.mod | 2 +- vendor.sum | 4 ++-- vendor/modules.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor.mod b/vendor.mod index 1f61fa1659..28543f6bf4 100644 --- a/vendor.mod +++ b/vendor.mod @@ -34,7 +34,7 @@ require ( github.com/mattn/go-runewidth v0.0.28 github.com/moby/go-archive v0.3.3 github.com/moby/moby/api v1.56.0 - github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 + github.com/moby/moby/client v0.6.0 github.com/moby/patternmatcher v0.6.1 github.com/moby/swarmkit/v2 v2.1.2 github.com/moby/sys/atomicwriter v0.1.0 diff --git a/vendor.sum b/vendor.sum index 12d4f04641..da779326f3 100644 --- a/vendor.sum +++ b/vendor.sum @@ -90,8 +90,8 @@ github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= github.com/moby/moby/api v1.56.0 h1:GQzua3NA599ASSIICx0iFgiJeO9YkdDARvQsm23ZZuQ= github.com/moby/moby/api v1.56.0/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk= -github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 h1:/XUuyJxH/HXe5MBcQo+YG9hAtZTQNpWFeJ5PcxL+hs8= -github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= +github.com/moby/moby/client v0.6.0 h1:AJjEB21QPbXSXjDsZorFBoDZPhMrfbpaPLgSMAW9Bgs= +github.com/moby/moby/client v0.6.0/go.mod h1:OCo00wNRyA3m4lmJ228W3JbyCN4ZNNYjpOXiJydBdcQ= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/swarmkit/v2 v2.1.2 h1:1WDZAI6HVYNKdCG4zlXnTAPyLsLwuhRGWlHoOUf5Z6I= diff --git a/vendor/modules.txt b/vendor/modules.txt index ef41fdffa8..47b2f72e14 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -190,7 +190,7 @@ github.com/moby/moby/api/types/storage github.com/moby/moby/api/types/swarm github.com/moby/moby/api/types/system github.com/moby/moby/api/types/volume -# github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 +# github.com/moby/moby/client v0.6.0 ## explicit; go 1.24 github.com/moby/moby/client github.com/moby/moby/client/internal From c50ce17c1946cc2a2c530f11c6a7a217f778219a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 3 Sep 2026 23:40:55 +0200 Subject: [PATCH 44/44] vendor: github.com/go-jose/go-jose/v4 v4.1.5 Fixed security issues - cipher/cbc_hmac: don't panic on empty ciphertext - cipher/cbc_hmac: don't panic on invalid key - json: limit stack depth - jwt: reject out-of-range NumericDate values - Check alg against pubkey curve during verify. - Reject malformed Ed25519 JWKs - jws: choose verification key per-signature Changed - Verify OpaqueSigner's Public() return is public - jws: skip signature on ErrJWKSKidNotFound - Handle JWE JSON without protected header - jws: don't strip internal whitespace before parsing JSON - jws: fewer calls to OpaqueSigner.Public() - Return a specific error when parsing empty string - Reject typed nil at Verify time full diff: https://github.com/go-jose/go-jose/compare/v4.1.4...v4.1.5 Signed-off-by: Sebastiaan van Stijn --- vendor.mod | 2 +- vendor.sum | 4 +- .../go-jose/go-jose/v4/asymmetric.go | 26 ++- .../go-jose/go-jose/v4/cipher/cbc_hmac.go | 6 +- .../github.com/go-jose/go-jose/v4/crypter.go | 90 +++++--- .../go-jose/go-jose/v4/json/scanner.go | 19 +- vendor/github.com/go-jose/go-jose/v4/jwe.go | 8 + vendor/github.com/go-jose/go-jose/v4/jwk.go | 213 ++++++++++++++---- vendor/github.com/go-jose/go-jose/v4/jws.go | 15 +- .../go-jose/go-jose/v4/jwt/claims.go | 15 ++ .../github.com/go-jose/go-jose/v4/opaque.go | 5 + .../github.com/go-jose/go-jose/v4/shared.go | 6 + .../github.com/go-jose/go-jose/v4/signing.go | 111 +++++---- .../go-jose/go-jose/v4/symmetric.go | 10 +- vendor/modules.txt | 2 +- 15 files changed, 388 insertions(+), 144 deletions(-) diff --git a/vendor.mod b/vendor.mod index 28543f6bf4..b864c533da 100644 --- a/vendor.mod +++ b/vendor.mod @@ -25,7 +25,7 @@ require ( github.com/docker/go-connections v0.8.1 github.com/docker/go-units v0.5.0 github.com/fvbommel/sortorder v1.2.0 - github.com/go-jose/go-jose/v4 v4.1.4 + github.com/go-jose/go-jose/v4 v4.1.5 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/gogo/protobuf v1.3.2 github.com/google/go-cmp v0.7.0 diff --git a/vendor.sum b/vendor.sum index da779326f3..72337adf30 100644 --- a/vendor.sum +++ b/vendor.sum @@ -51,8 +51,8 @@ github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeO github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/fvbommel/sortorder v1.2.0 h1:TRIiRiGX+djh3Yf4FVxmWmAcYfIr5dH0NbzJWOSAWZk= github.com/fvbommel/sortorder v1.2.0/go.mod h1:LbhO04ijZIeUuvz9B9BkI/qYrpZZEn1gWhxv4QjUKVs= -github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA= +github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= diff --git a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go index 7784cd4584..2b5607617e 100644 --- a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go +++ b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go @@ -21,6 +21,7 @@ import ( "crypto/aes" "crypto/ecdsa" "crypto/ed25519" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha1" @@ -195,11 +196,11 @@ func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipi func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) { switch alg { case RSA1_5: - return rsa.EncryptPKCS1v15(RandReader, ctx.publicKey, cek) + return rsa.EncryptPKCS1v15(randReader, ctx.publicKey, cek) case RSA_OAEP: - return rsa.EncryptOAEP(sha1.New(), RandReader, ctx.publicKey, cek, []byte{}) + return rsa.EncryptOAEP(sha1.New(), randReader, ctx.publicKey, cek, []byte{}) case RSA_OAEP_256: - return rsa.EncryptOAEP(sha256.New(), RandReader, ctx.publicKey, cek, []byte{}) + return rsa.EncryptOAEP(sha256.New(), randReader, ctx.publicKey, cek, []byte{}) } return nil, ErrUnsupportedAlgorithm @@ -288,9 +289,9 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm // TODO(https://github.com/go-jose/go-jose/issues/40): As of go1.20, the // random parameter is legacy and ignored, and it can be nil. // https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/rsa/pkcs1v15.go;l=263;bpv=0;bpt=1 - out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed) + out, err = rsa.SignPKCS1v15(randReader, ctx.privateKey, hash, hashed) case PS256, PS384, PS512: - out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ + out, err = rsa.SignPSS(randReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) } @@ -391,7 +392,7 @@ func (ctx ecKeyGenerator) keySize() int { // Get a content encryption key for ECDH-ES func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) { - priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, RandReader) + priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, randReader) if err != nil { return nil, rawHeader{}, err } @@ -483,7 +484,7 @@ func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) return Signature{}, ErrUnsupportedAlgorithm } - sig, err := ctx.privateKey.Sign(RandReader, payload, crypto.Hash(0)) + sig, err := ctx.privateKey.Sign(randReader, payload, crypto.Hash(0)) if err != nil { return Signature{}, err } @@ -533,7 +534,7 @@ func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) _, _ = hasher.Write(payload) hashed := hasher.Sum(nil) - r, s, err := ecdsa.Sign(RandReader, ctx.privateKey, hashed) + r, s, err := ecdsa.Sign(randReader, ctx.privateKey, hashed) if err != nil { return Signature{}, err } @@ -571,12 +572,21 @@ func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, a case ES256: keySize = 32 hash = crypto.SHA256 + if ctx.publicKey.Curve != elliptic.P256() { + return fmt.Errorf("go-jose/go-jose: signature uses different algorithm than public key") + } case ES384: keySize = 48 hash = crypto.SHA384 + if ctx.publicKey.Curve != elliptic.P384() { + return fmt.Errorf("go-jose/go-jose: signature uses different algorithm than public key") + } case ES512: keySize = 66 hash = crypto.SHA512 + if ctx.publicKey.Curve != elliptic.P521() { + return fmt.Errorf("go-jose/go-jose: signature uses different algorithm than public key") + } default: return ErrUnsupportedAlgorithm } diff --git a/vendor/github.com/go-jose/go-jose/v4/cipher/cbc_hmac.go b/vendor/github.com/go-jose/go-jose/v4/cipher/cbc_hmac.go index af029cec0b..a301ad9e20 100644 --- a/vendor/github.com/go-jose/go-jose/v4/cipher/cbc_hmac.go +++ b/vendor/github.com/go-jose/go-jose/v4/cipher/cbc_hmac.go @@ -51,6 +51,8 @@ func NewCBCHMAC(key []byte, newBlockCipher func([]byte) (cipher.Block, error)) ( hash = sha512.New384 case 32: hash = sha512.New + default: + return nil, errors.New("go-jose/go-jose: invalid key size for CBC-HMAC") } return &cbcAEAD{ @@ -176,7 +178,9 @@ func padBuffer(buffer []byte, blockSize int) []byte { // Remove padding func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) { - if len(buffer)%blockSize != 0 { + // A padded buffer can't be empty because an empty input is padded with + // `blockSize` bytes, resulting in a non-empty ciphertext. + if len(buffer) == 0 || len(buffer)%blockSize != 0 { return nil, errors.New("go-jose/go-jose: invalid padding") } diff --git a/vendor/github.com/go-jose/go-jose/v4/crypter.go b/vendor/github.com/go-jose/go-jose/v4/crypter.go index 31290fc871..e3df79c2dc 100644 --- a/vendor/github.com/go-jose/go-jose/v4/crypter.go +++ b/vendor/github.com/go-jose/go-jose/v4/crypter.go @@ -258,6 +258,10 @@ func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) { } recipientInfo, err = makeJWERecipient(recipient.Algorithm, recipient.Key) + if err != nil { + return err + } + if recipient.KeyID != "" { recipientInfo.keyID = recipient.KeyID } @@ -270,10 +274,8 @@ func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) { } } - if err == nil { - ctx.recipients = append(ctx.recipients, recipientInfo) - } - return err + ctx.recipients = append(ctx.recipients, recipientInfo) + return nil } func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKeyInfo, error) { @@ -490,21 +492,19 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) recipientHeaders := obj.mergedHeaders(&recipient) cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) - if err == nil { - // Found a valid CEK -- let's try to decrypt. - plaintext, err = cipher.decrypt(cek, authData, parts) - } - - if plaintext == nil { + if err != nil { return nil, ErrCryptoFailure } - // The "zip" header parameter may only be present in the protected header. - if comp := obj.protected.getCompression(); comp != "" { - plaintext, err = decompress(comp, plaintext) - if err != nil { - return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err) - } + // Found a valid CEK -- let's try to decrypt. + plaintext, err = cipher.decrypt(cek, authData, parts) + if err != nil { + return nil, ErrCryptoFailure + } + + plaintext, err = obj.decompress(plaintext) + if err != nil { + return nil, err } return plaintext, nil @@ -559,31 +559,38 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade var plaintext []byte var headers rawHeader + if len(obj.recipients) == 0 { + return -1, Header{}, nil, errors.New("go-jose/go-jose: no recipients") + } + + // Loop sets `err` in the function scope; don't shadow it. for i, recipient := range obj.recipients { recipientHeaders := obj.mergedHeaders(&recipient) - cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) - if err == nil { - // Found a valid CEK -- let's try to decrypt. - plaintext, err = cipher.decrypt(cek, authData, parts) - if err == nil { - index = i - headers = recipientHeaders - break - } + var cek []byte + cek, err = decrypter.decryptKey(recipientHeaders, &recipient, generator) + if err != nil { + continue } + + // Found a valid CEK -- let's try to decrypt. + plaintext, err = cipher.decrypt(cek, authData, parts) + if err != nil { + continue + } + + index = i + headers = recipientHeaders + break } - if plaintext == nil { + if err != nil { return -1, Header{}, nil, ErrCryptoFailure } - // The "zip" header parameter may only be present in the protected header. - if comp := obj.protected.getCompression(); comp != "" { - plaintext, err = decompress(comp, plaintext) - if err != nil { - return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err) - } + plaintext, err = obj.decompress(plaintext) + if err != nil { + return -1, Header{}, nil, err } sanitized, err := headers.sanitized() @@ -593,3 +600,22 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade return index, sanitized, plaintext, err } + +// decompress decompresses plaintext using the protected "zip" header, if present. +// It returns plaintext unchanged when there is no protected header or "zip" value. +func (obj JSONWebEncryption) decompress(plaintext []byte) ([]byte, error) { + if obj.protected == nil { + return plaintext, nil + } + + comp := obj.protected.getCompression() + if comp == "" { + return plaintext, nil + } + + plaintext, err := decompress(comp, plaintext) + if err != nil { + return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err) + } + return plaintext, nil +} diff --git a/vendor/github.com/go-jose/go-jose/v4/json/scanner.go b/vendor/github.com/go-jose/go-jose/v4/json/scanner.go index ee6622e8cf..3bd29f397a 100644 --- a/vendor/github.com/go-jose/go-jose/v4/json/scanner.go +++ b/vendor/github.com/go-jose/go-jose/v4/json/scanner.go @@ -140,6 +140,10 @@ const ( parseArrayValue // parsing array value ) +// This limits the max nesting depth to prevent stack overflow. +// This is permitted by https://tools.ietf.org/html/rfc7159#section-9 +const maxNestingDepth = 10000 + // reset prepares the scanner for use. // It must be called before calling s.step. func (s *scanner) reset() { @@ -170,8 +174,13 @@ func (s *scanner) eof() int { } // pushParseState pushes a new parse state p onto the parse stack. -func (s *scanner) pushParseState(p int) { - s.parseState = append(s.parseState, p) +// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned. +func (s *scanner) pushParseState(c byte, newParseState int, successState int) int { + s.parseState = append(s.parseState, newParseState) + if len(s.parseState) <= maxNestingDepth { + return successState + } + return s.error(c, "exceeded max depth") } // popParseState pops a parse state (already obtained) off the stack @@ -211,12 +220,10 @@ func stateBeginValue(s *scanner, c byte) int { switch c { case '{': s.step = stateBeginStringOrEmpty - s.pushParseState(parseObjectKey) - return scanBeginObject + return s.pushParseState(c, parseObjectKey, scanBeginObject) case '[': s.step = stateBeginValueOrEmpty - s.pushParseState(parseArrayValue) - return scanBeginArray + return s.pushParseState(c, parseArrayValue, scanBeginArray) case '"': s.step = stateInString return scanBeginLiteral diff --git a/vendor/github.com/go-jose/go-jose/v4/jwe.go b/vendor/github.com/go-jose/go-jose/v4/jwe.go index 6102f91000..ae173461b7 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jwe.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwe.go @@ -153,6 +153,10 @@ func ParseEncryptedJSON( keyEncryptionAlgorithms []KeyAlgorithm, contentEncryption []ContentEncryption, ) (*JSONWebEncryption, error) { + if len(input) == 0 { + return nil, errEmptyInput + } + var parsed rawJSONWebEncryption err := json.Unmarshal([]byte(input), &parsed) if err != nil { @@ -291,6 +295,10 @@ func ParseEncryptedCompact( var parts [5]string var ok bool + if len(input) == 0 { + return nil, errEmptyInput + } + for i := range 4 { parts[i], input, ok = strings.Cut(input, ".") if !ok { diff --git a/vendor/github.com/go-jose/go-jose/v4/jwk.go b/vendor/github.com/go-jose/go-jose/v4/jwk.go index 164d6a1619..f712ec7ec8 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jwk.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwk.go @@ -107,7 +107,7 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { switch key := k.Key.(type) { case ed25519.PublicKey: - raw = fromEdPublicKey(key) + raw, err = fromEdPublicKey(key) case *ecdsa.PublicKey: raw, err = fromEcPublicKey(key) case *rsa.PublicKey: @@ -207,21 +207,29 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() - if err == nil { - keyPub = key.(*ecdsa.PrivateKey).Public() + if err != nil { + return err } + keyPub = key.(*ecdsa.PrivateKey).Public() } else { key, err = raw.ecPublicKey() + if err != nil { + return err + } keyPub = key } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() - if err == nil { - keyPub = key.(*rsa.PrivateKey).Public() + if err != nil { + return err } + keyPub = key.(*rsa.PrivateKey).Public() } else { key, err = raw.rsaPublicKey() + if err != nil { + return err + } keyPub = key } case "oct": @@ -229,25 +237,28 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { return errors.New("go-jose/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") } key, err = raw.symmetricKey() + if err != nil { + return err + } case "OKP": if raw.Crv == "Ed25519" { if raw.D != nil { key, err = raw.edPrivateKey() - if err == nil { - keyPub = key.(ed25519.PrivateKey).Public() + if err != nil { + return err } + keyPub = key.(ed25519.PrivateKey).Public() } else { key, err = raw.edPublicKey() + if err != nil { + return err + } keyPub = key } } case "": // kty MUST be present - err = fmt.Errorf("go-jose/go-jose: missing json web key type") - } - - if err != nil { - return + return fmt.Errorf("go-jose/go-jose: missing json web key type") } if key == nil { @@ -388,11 +399,12 @@ func rsaThumbprintInput(n *big.Int, e int) (string, error) { func edThumbprintInput(ed ed25519.PublicKey) (string, error) { crv := "Ed25519" - if len(ed) > 32 { - return "", errors.New("go-jose/go-jose: invalid elliptic key (too large)") + err := validateEd25519PublicKey(ed) + if err != nil { + return "", err } return fmt.Sprintf(edThumbprintTemplate, crv, - newFixedSizeBuffer(ed, 32).base64()), nil + newFixedSizeBuffer(ed, ed25519.PublicKeySize).base64()), nil } // Thumbprint computes the JWK Thumbprint of a key using the @@ -504,12 +516,16 @@ func (key rawJSONWebKey) rsaPublicKey() (*rsa.PublicKey, error) { }, nil } -func fromEdPublicKey(pub ed25519.PublicKey) *rawJSONWebKey { +func fromEdPublicKey(pub ed25519.PublicKey) (*rawJSONWebKey, error) { + err := validateEd25519PublicKey(pub) + if err != nil { + return nil, err + } return &rawJSONWebKey{ Kty: "OKP", Crv: "Ed25519", X: newBuffer(pub), - } + }, nil } func fromRsaPublicKey(pub *rsa.PublicKey) *rawJSONWebKey { @@ -604,21 +620,36 @@ func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) { return nil, fmt.Errorf("go-jose/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", ")) } - privateKey := make([]byte, ed25519.PrivateKeySize) - copy(privateKey[0:32], key.D.bytes()) - copy(privateKey[32:], key.X.bytes()) - rv := ed25519.PrivateKey(privateKey) - return rv, nil + publicKey := key.X.bytes() + err := validateEd25519PublicKey(publicKey) + if err != nil { + return nil, err + } + + seed := key.D.bytes() + if len(seed) != ed25519.SeedSize { + return nil, fmt.Errorf("go-jose/go-jose: invalid Ed25519 private key, wrong length for d") + } + + privateKey := ed25519.NewKeyFromSeed(seed) + derivedPublicKey := privateKey.Public().(ed25519.PublicKey) + if !bytes.Equal(derivedPublicKey, publicKey) { + return nil, errors.New("go-jose/go-jose: invalid Ed25519 private key, x does not match d") + } + + return privateKey, nil } func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) { if key.X == nil { return nil, fmt.Errorf("go-jose/go-jose: invalid Ed key, missing x value") } - publicKey := make([]byte, ed25519.PublicKeySize) - copy(publicKey[0:32], key.X.bytes()) - rv := ed25519.PublicKey(publicKey) - return rv, nil + publicKey := key.X.bytes() + err := validateEd25519PublicKey(publicKey) + if err != nil { + return nil, err + } + return ed25519.PublicKey(bytes.Clone(publicKey)), nil } func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { @@ -669,9 +700,17 @@ func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { } func fromEdPrivateKey(ed ed25519.PrivateKey) (*rawJSONWebKey, error) { - raw := fromEdPublicKey(ed25519.PublicKey(ed[32:])) + if len(ed) != ed25519.PrivateKeySize { + return nil, errors.New("go-jose/go-jose: invalid Ed25519 private key length") + } - raw.D = newBuffer(ed[0:32]) + publicKey := ed.Public().(ed25519.PublicKey) + raw, err := fromEdPublicKey(publicKey) + if err != nil { + return nil, err + } + + raw.D = newBuffer(ed.Seed()) return raw, nil } @@ -810,7 +849,7 @@ var ( ErrJWKSKidNotFound = errors.New("go-jose/go-jose: JWK with matching kid not found in JWK Set") ) -func tryJWKS(key interface{}, headers ...Header) (interface{}, error) { +func tryJWKS(key interface{}, header Header) (interface{}, error) { var jwks JSONWebKeySet switch jwksType := key.(type) { @@ -823,16 +862,8 @@ func tryJWKS(key interface{}, headers ...Header) (interface{}, error) { return key, nil } - // Determine the KID to search for from the headers. - var kid string - for _, header := range headers { - if header.KeyID != "" { - kid = header.KeyID - break - } - } - - // If no KID is specified in the headers, reject. + // If no KID is specified in the header, reject. + kid := header.KeyID if kid == "" { return nil, ErrJWKSKidNotFound } @@ -846,3 +877,107 @@ func tryJWKS(key interface{}, headers ...Header) (interface{}, error) { return keys[0].Key, nil } + +// weakEd25519PublicKeys contains the low-order Ed25519 encodings accepted by +// Go's verifier that must not be accepted as JOSE verification keys. +var weakEd25519PublicKeys = map[[ed25519.PublicKeySize]byte]struct{}{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }: {}, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + }: {}, + { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }: {}, + { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + }: {}, + { + 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, + 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, + 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, + 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05, + }: {}, + { + 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, + 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, + 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, + 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85, + }: {}, + { + 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, + 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, + 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, + 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a, + }: {}, + { + 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, + 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, + 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, + 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa, + }: {}, + { + 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }: {}, + { + 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }: {}, + { + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }: {}, + { + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }: {}, + { + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }: {}, + { + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }: {}, +} + +func validateEd25519PublicKey(publicKey ed25519.PublicKey) error { + if len(publicKey) != ed25519.PublicKeySize { + return fmt.Errorf("go-jose/go-jose: invalid Ed25519 public key, wrong length for x") + } + + var encoded [ed25519.PublicKeySize]byte + copy(encoded[:], publicKey) + _, ok := weakEd25519PublicKeys[encoded] + if ok { + return errors.New("go-jose/go-jose: invalid Ed25519 public key, low-order point") + } + + return nil +} diff --git a/vendor/github.com/go-jose/go-jose/v4/jws.go b/vendor/github.com/go-jose/go-jose/v4/jws.go index c40bd3ec10..0444a5da05 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jws.go +++ b/vendor/github.com/go-jose/go-jose/v4/jws.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "strings" + "unicode" "github.com/go-jose/go-jose/v4/json" ) @@ -89,12 +90,12 @@ func ParseSigned( signature string, signatureAlgorithms []SignatureAlgorithm, ) (*JSONWebSignature, error) { - signature = stripWhitespace(signature) - if strings.HasPrefix(signature, "{") { + trimmed := strings.TrimLeftFunc(signature, unicode.IsSpace) + if strings.HasPrefix(trimmed, "{") { return ParseSignedJSON(signature, signatureAlgorithms) } - return parseSignedCompact(signature, nil, signatureAlgorithms) + return parseSignedCompact(stripWhitespace(signature), nil, signatureAlgorithms) } // ParseSignedCompact parses a message in JWS Compact Serialization. Validation fails if the JWS is @@ -186,6 +187,10 @@ func ParseSignedJSON( input string, signatureAlgorithms []SignatureAlgorithm, ) (*JSONWebSignature, error) { + if len(input) == 0 { + return nil, errEmptyInput + } + var parsed rawJSONWebSignature err := json.Unmarshal([]byte(input), &parsed) if err != nil { @@ -369,6 +374,10 @@ func parseSignedCompact( payload []byte, signatureAlgorithms []SignatureAlgorithm, ) (*JSONWebSignature, error) { + if len(input) == 0 { + return nil, errEmptyInput + } + protected, s, ok := strings.Cut(input, tokenDelim) if !ok { // no period found return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts") diff --git a/vendor/github.com/go-jose/go-jose/v4/jwt/claims.go b/vendor/github.com/go-jose/go-jose/v4/jwt/claims.go index e73412a391..ec199e29a4 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jwt/claims.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwt/claims.go @@ -24,6 +24,11 @@ import ( "github.com/go-jose/go-jose/v4/json" ) +// maxNumericDate bounds an accepted NumericDate (in seconds). It is far past +// any real timestamp but safely below where an int64 conversion or time.Unix +// would overflow and wrap Time() to a bogus instant. +const maxNumericDate = 1 << 62 + // Claims represents public claim values (as specified in RFC 7519). type Claims struct { Issuer string `json:"iss,omitempty"` @@ -69,6 +74,16 @@ func (n *NumericDate) UnmarshalJSON(b []byte) error { return ErrUnmarshalNumericDate } + // Reject values large enough to overflow either the int64 conversion below + // or time.Unix in Time(). time.Unix adds a ~62e9-second offset internally, + // so a value near the int64 limit wraps and compares as a time in the + // past; for "nbf" that lets a not-yet-valid token pass the not-before + // check instead of being rejected. maxNumericDate (2^62 seconds) is far + // beyond any real timestamp yet safely below both overflow points. + if f >= maxNumericDate || f <= -maxNumericDate { + return ErrUnmarshalNumericDate + } + *n = NumericDate(f) return nil } diff --git a/vendor/github.com/go-jose/go-jose/v4/opaque.go b/vendor/github.com/go-jose/go-jose/v4/opaque.go index 429427232e..91a42fc1dc 100644 --- a/vendor/github.com/go-jose/go-jose/v4/opaque.go +++ b/vendor/github.com/go-jose/go-jose/v4/opaque.go @@ -46,6 +46,11 @@ func newOpaqueSigner(alg SignatureAlgorithm, signer OpaqueSigner) (recipientSigI return recipientSigInfo{}, ErrUnsupportedAlgorithm } + pk := signer.Public() + if pk != nil && !pk.IsPublic() { + return recipientSigInfo{}, ErrNotPublic + } + return recipientSigInfo{ sigAlg: alg, publicKey: signer.Public, diff --git a/vendor/github.com/go-jose/go-jose/v4/shared.go b/vendor/github.com/go-jose/go-jose/v4/shared.go index 35130b3aa8..3df242ca6a 100644 --- a/vendor/github.com/go-jose/go-jose/v4/shared.go +++ b/vendor/github.com/go-jose/go-jose/v4/shared.go @@ -80,6 +80,12 @@ var ( // ErrUnsupportedCriticalHeader is returned when a header is marked critical but not supported by go-jose. ErrUnsupportedCriticalHeader = errors.New("go-jose/go-jose: unsupported critical header") + + // errEmptyInput is returned when go-jose was asked to parse an empty string. + errEmptyInput = errors.New("go-jose/go-jose: empty input") + + // ErrNotPublic indicates a private key was passed where a public key was expected + ErrNotPublic = errors.New("go-jose/go-jose: public key was unexpectedly not public") ) // Key management algorithms diff --git a/vendor/github.com/go-jose/go-jose/v4/signing.go b/vendor/github.com/go-jose/go-jose/v4/signing.go index 5dbd04c278..5baf75cb2e 100644 --- a/vendor/github.com/go-jose/go-jose/v4/signing.go +++ b/vendor/github.com/go-jose/go-jose/v4/signing.go @@ -130,6 +130,8 @@ type payloadVerifier interface { verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error } +var errInvalidVerificationKey = errors.New("go-jose/go-jose: invalid verification key") + type genericSigner struct { recipients []recipientSigInfo nonceSource NonceSource @@ -138,11 +140,21 @@ type genericSigner struct { } type recipientSigInfo struct { - sigAlg SignatureAlgorithm + sigAlg SignatureAlgorithm + // publicKey returns a synthetic JSONWebKey for the signer. + // For opaque signers, it calls OpaqueSigner.Public(). publicKey func() *JSONWebKey signer payloadSigner } +// getPublicKey gets the public key, with a nil check on the func. +func (r recipientSigInfo) getPublicKey() *JSONWebKey { + if r.publicKey == nil { + return nil + } + return r.publicKey() +} + func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey { return func() *JSONWebKey { return jwk @@ -178,14 +190,23 @@ func NewMultiSigner(sigs []SigningKey, opts *SignerOptions) (Signer, error) { func newVerifier(verificationKey interface{}) (payloadVerifier, error) { switch verificationKey := verificationKey.(type) { case ed25519.PublicKey: + if len(verificationKey) == 0 { + return nil, errInvalidVerificationKey + } return &edEncrypterVerifier{ publicKey: verificationKey, }, nil case *rsa.PublicKey: + if verificationKey == nil { + return nil, errInvalidVerificationKey + } return &rsaEncrypterVerifier{ publicKey: verificationKey, }, nil case *ecdsa.PublicKey: + if verificationKey == nil { + return nil, errInvalidVerificationKey + } return &ecEncrypterVerifier{ publicKey: verificationKey, }, nil @@ -196,6 +217,9 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) { case JSONWebKey: return newVerifier(verificationKey.Key) case *JSONWebKey: + if verificationKey == nil { + return nil, errInvalidVerificationKey + } return newVerifier(verificationKey.Key) case OpaqueVerifier: return &opaqueVerifier{verifier: verificationKey}, nil @@ -240,18 +264,18 @@ func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigIn if err != nil { return recipientSigInfo{}, err } - if recipient.publicKey != nil && recipient.publicKey() != nil { + if recipientPubKey := recipient.getPublicKey(); recipientPubKey != nil { + // This should be impossible, but let's check anyway. + if !recipientPubKey.IsPublic() { + return recipientSigInfo{}, ErrNotPublic + } + // recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo // was created for the inner key (such as a RSA or ECDSA public key). It contains // the pub key for embedding, but doesn't have extra params like key id. publicKey := signingKey - publicKey.Key = recipient.publicKey().Key + publicKey.Key = recipientPubKey.Key recipient.publicKey = staticPublicKey(&publicKey) - - // This should be impossible, but let's check anyway. - if !recipient.publicKey().IsPublic() { - return recipientSigInfo{}, errors.New("go-jose/go-jose: public key was unexpectedly not public") - } } return recipient, nil } @@ -266,7 +290,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { headerAlgorithm: string(recipient.sigAlg), } - if recipient.publicKey != nil && recipient.publicKey() != nil { + if recipientPubKey := recipient.getPublicKey(); recipientPubKey != nil { // We want to embed the JWK or set the kid header, but not both. Having a protected // header that contains an embedded JWK while also simultaneously containing the kid // header is confusing, and at least in ACME the two are considered to be mutually @@ -274,11 +298,11 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { // result of the JOSE spec. We've decided that this library will only include one or // the other to avoid this confusion. // - // See https://github.com/go-jose/go-jose/issues/157 for more context. + // See https://github.com/square/go-jose/issues/157 for more context. if ctx.embedJWK { - protected[headerJWK] = recipient.publicKey() + protected[headerJWK] = recipientPubKey } else { - keyID := recipient.publicKey().KeyID + keyID := recipientPubKey.KeyID if keyID != "" { protected[headerKeyID] = keyID } @@ -390,7 +414,13 @@ func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte { // The verificationKey argument must have one of the types allowed for the // verificationKey argument of JSONWebSignature.Verify(). func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error { - key, err := tryJWKS(verificationKey, obj.headers()...) + if len(obj.Signatures) > 1 { + return errors.New("go-jose/go-jose: too many signatures in payload; expecting only one") + } + + signature := obj.Signatures[0] + + key, err := tryJWKS(verificationKey, signature.Header) if err != nil { return err } @@ -399,12 +429,6 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter return err } - if len(obj.Signatures) > 1 { - return errors.New("go-jose/go-jose: too many signatures in payload; expecting only one") - } - - signature := obj.Signatures[0] - if signature.header != nil { // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, // 4.1.11. "crit" (Critical) Header Parameter @@ -432,11 +456,11 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter headers := signature.mergedHeaders() alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) - if err == nil { - return nil + if err != nil { + return ErrCryptoFailure } - return ErrCryptoFailure + return nil } // VerifyMulti validates (one of the multiple) signatures on the object and @@ -467,16 +491,6 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa // The verificationKey argument must have one of the types allowed for the // verificationKey argument of JSONWebSignature.Verify(). func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) { - key, err := tryJWKS(verificationKey, obj.headers()...) - if err != nil { - return -1, Signature{}, err - } - verifier, err := newVerifier(key) - if err != nil { - return -1, Signature{}, err - } - -outer: for i, signature := range obj.Signatures { if signature.header != nil { // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, @@ -484,20 +498,31 @@ outer: // "When used, this Header Parameter MUST be integrity // protected; therefore, it MUST occur only within the JWS // Protected Header." - err = signature.header.checkNoCritical() + err := signature.header.checkNoCritical() if err != nil { - continue outer + continue } } if signature.protected != nil { // Check for only supported critical headers - err = signature.protected.checkSupportedCritical(supportedCritical) + err := signature.protected.checkSupportedCritical(supportedCritical) if err != nil { - continue outer + continue } } + // If the verification key is a JWK Set, pick a key based on this signature's + // "kid" header. If no match, skip this signature. + key, err := tryJWKS(verificationKey, signature.Header) + if err != nil { + continue + } + verifier, err := newVerifier(key) + if err != nil { + continue + } + input, err := obj.computeAuthData(payload, &signature) if err != nil { continue @@ -506,18 +531,12 @@ outer: headers := signature.mergedHeaders() alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) - if err == nil { - return i, signature, nil + if err != nil { + continue } + + return i, signature, nil } return -1, Signature{}, ErrCryptoFailure } - -func (obj JSONWebSignature) headers() []Header { - headers := make([]Header, len(obj.Signatures)) - for i, sig := range obj.Signatures { - headers[i] = sig.Header - } - return headers -} diff --git a/vendor/github.com/go-jose/go-jose/v4/symmetric.go b/vendor/github.com/go-jose/go-jose/v4/symmetric.go index f2ff29e179..ca597d6a12 100644 --- a/vendor/github.com/go-jose/go-jose/v4/symmetric.go +++ b/vendor/github.com/go-jose/go-jose/v4/symmetric.go @@ -34,8 +34,8 @@ import ( josecipher "github.com/go-jose/go-jose/v4/cipher" ) -// RandReader is a cryptographically secure random number generator (stubbed out in tests). -var RandReader = rand.Reader +// randReader is a cryptographically secure random number generator (stubbed out in tests). +var randReader = rand.Reader const ( // RFC7518 recommends a minimum of 1,000 iterations: @@ -153,7 +153,7 @@ func getPbkdf2Params(alg KeyAlgorithm) (int, func() hash.Hash) { // getRandomSalt generates a new salt of the given size. func getRandomSalt(size int) ([]byte, error) { salt := make([]byte, size) - _, err := io.ReadFull(RandReader, salt) + _, err := io.ReadFull(randReader, salt) if err != nil { return nil, err } @@ -198,7 +198,7 @@ func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo // Generate a random key for the given content cipher func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) { key := make([]byte, ctx.size) - _, err := io.ReadFull(RandReader, key) + _, err := io.ReadFull(randReader, key) if err != nil { return nil, rawHeader{}, err } @@ -238,7 +238,7 @@ func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) { // Initialize a new nonce iv := make([]byte, aead.NonceSize()) - _, err = io.ReadFull(RandReader, iv) + _, err = io.ReadFull(randReader, iv) if err != nil { return nil, err } diff --git a/vendor/modules.txt b/vendor/modules.txt index 47b2f72e14..f71feb45f3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -94,7 +94,7 @@ github.com/felixge/httpsnoop # github.com/fvbommel/sortorder v1.2.0 ## explicit; go 1.21 github.com/fvbommel/sortorder -# github.com/go-jose/go-jose/v4 v4.1.4 +# github.com/go-jose/go-jose/v4 v4.1.5 ## explicit; go 1.24.0 github.com/go-jose/go-jose/v4 github.com/go-jose/go-jose/v4/cipher