From 5f667b4fb78a611f41c8f7b5d58a87695d77df7b Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:41:45 +0200 Subject: [PATCH] progress: replace hashstructure with explicit warning comparison Deduplicate warnings without mutating their source definitions and preserve the order of first occurrences. Remove the hashstructure dependency and add coverage for warning equality and input preservation. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- go.mod | 1 - go.sum | 2 - util/progress/printer.go | 45 +- util/progress/printer_test.go | 69 +++ .../mitchellh/hashstructure/v2/LICENSE | 21 - .../mitchellh/hashstructure/v2/README.md | 76 --- .../mitchellh/hashstructure/v2/errors.go | 22 - .../hashstructure/v2/hashstructure.go | 482 ------------------ .../mitchellh/hashstructure/v2/include.go | 22 - vendor/modules.txt | 3 - 10 files changed, 97 insertions(+), 646 deletions(-) create mode 100644 util/progress/printer_test.go delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/LICENSE delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/README.md delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/errors.go delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/include.go diff --git a/go.mod b/go.mod index 2ed8005ab073..b5a273489ab9 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928 github.com/hashicorp/hcl/v2 v2.24.0 github.com/in-toto/in-toto-golang v0.11.0 - github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/moby/buildkit v0.33.0 github.com/moby/moby/api v1.55.0 github.com/moby/moby/client v0.5.0 diff --git a/go.sum b/go.sum index e4b03368b3c0..655d9329b8eb 100644 --- a/go.sum +++ b/go.sum @@ -389,8 +389,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/buildkit v0.33.0 h1:zBbt1FiMcTB/oFg1iCNcKa83k5Rn8MGcVjXFIcfYhuQ= diff --git a/util/progress/printer.go b/util/progress/printer.go index dcc2294569c1..eec6020e3024 100644 --- a/util/progress/printer.go +++ b/util/progress/printer.go @@ -1,19 +1,22 @@ package progress import ( + "bytes" "context" "io" "os" + "slices" "sync" "github.com/docker/buildx/util/logutil" - "github.com/mitchellh/hashstructure/v2" "github.com/moby/buildkit/client" + "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/progress/progressui" "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "google.golang.org/protobuf/proto" ) type printerState int @@ -295,28 +298,36 @@ func WithOnClose(onclose func()) PrinterOpt { } func dedupWarnings(inp []client.VertexWarning) []client.VertexWarning { - m := make(map[uint64]client.VertexWarning) + res := make([]client.VertexWarning, 0, len(inp)) for _, w := range inp { - wcp := w - wcp.Vertex = "" - if wcp.SourceInfo != nil { - wcp.SourceInfo.Definition = nil + if !slices.ContainsFunc(res, func(prev client.VertexWarning) bool { + return equalWarnings(prev, w) + }) { + res = append(res, w) } - h, err := hashstructure.Hash(wcp, hashstructure.FormatV2, nil) - if err != nil { - continue - } - if _, ok := m[h]; !ok { - m[h] = w - } - } - res := make([]client.VertexWarning, 0, len(m)) - for _, w := range m { - res = append(res, w) } return res } +// equalWarnings ignores the vertex and source definition, which can differ +// between otherwise identical warnings from separate builds. +func equalWarnings(a, b client.VertexWarning) bool { + if a.Level != b.Level || a.URL != b.URL || !bytes.Equal(a.Short, b.Short) || + !slices.EqualFunc(a.Detail, b.Detail, bytes.Equal) { + return false + } + if (a.SourceInfo == nil) != (b.SourceInfo == nil) { + return false + } + if a.SourceInfo != nil && (a.SourceInfo.Filename != b.SourceInfo.Filename || + a.SourceInfo.Language != b.SourceInfo.Language || !bytes.Equal(a.SourceInfo.Data, b.SourceInfo.Data)) { + return false + } + return slices.EqualFunc(a.Range, b.Range, func(a, b *pb.Range) bool { + return proto.Equal(a, b) + }) +} + type interruptRequest struct { desiredState printerState done chan<- struct{} diff --git a/util/progress/printer_test.go b/util/progress/printer_test.go new file mode 100644 index 000000000000..99d5b8f23851 --- /dev/null +++ b/util/progress/printer_test.go @@ -0,0 +1,69 @@ +package progress + +import ( + "testing" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/solver/pb" + "github.com/stretchr/testify/require" +) + +func TestDedupWarnings(t *testing.T) { + newWarning := func() client.VertexWarning { + return client.VertexWarning{ + Vertex: "first", + Level: 1, + Short: []byte("warning"), + Detail: [][]byte{[]byte("detail"), []byte("more detail")}, + URL: "https://example.com", + SourceInfo: &pb.SourceInfo{ + Filename: "Dockerfile", + Language: "dockerfile", + Data: []byte("FROM scratch"), + Definition: &pb.Definition{Def: [][]byte{[]byte("definition")}}, + }, + Range: []*pb.Range{{Start: &pb.Position{Line: 1}, End: &pb.Position{Line: 2}}}, + } + } + + t.Run("duplicates preserve first warning and inputs", func(t *testing.T) { + first, second := newWarning(), newWarning() + second.Vertex = "second" + second.SourceInfo.Definition = &pb.Definition{Def: [][]byte{[]byte("other definition")}} + got := dedupWarnings([]client.VertexWarning{first, second, first}) + require.Len(t, got, 1) + require.Equal(t, first, got[0]) + require.Equal(t, [][]byte{[]byte("definition")}, first.SourceInfo.Definition.Def) + require.Equal(t, [][]byte{[]byte("other definition")}, second.SourceInfo.Definition.Def) + }) + + for _, tc := range []struct { + name string + change func(*client.VertexWarning) + }{ + {"level", func(w *client.VertexWarning) { w.Level++ }}, + {"short", func(w *client.VertexWarning) { w.Short[0]++ }}, + {"detail", func(w *client.VertexWarning) { w.Detail[0][0]++ }}, + {"detail order", func(w *client.VertexWarning) { w.Detail[0], w.Detail[1] = w.Detail[1], w.Detail[0] }}, + {"url", func(w *client.VertexWarning) { w.URL += "/other" }}, + {"filename", func(w *client.VertexWarning) { w.SourceInfo.Filename += ".other" }}, + {"language", func(w *client.VertexWarning) { w.SourceInfo.Language = "other" }}, + {"source data", func(w *client.VertexWarning) { w.SourceInfo.Data[0]++ }}, + {"missing source", func(w *client.VertexWarning) { w.SourceInfo = nil }}, + {"range start", func(w *client.VertexWarning) { w.Range[0].Start.Line++ }}, + {"range end", func(w *client.VertexWarning) { w.Range[0].End.Character++ }}, + {"missing range", func(w *client.VertexWarning) { w.Range = nil }}, + } { + t.Run(tc.name, func(t *testing.T) { + first, second := newWarning(), newWarning() + tc.change(&second) + require.Equal(t, []client.VertexWarning{first, second}, dedupWarnings([]client.VertexWarning{first, second, first})) + }) + } + + t.Run("empty values", func(t *testing.T) { + require.Empty(t, dedupWarnings(nil)) + require.Len(t, dedupWarnings([]client.VertexWarning{{}, {Short: []byte{}, Detail: [][]byte{}, Range: []*pb.Range{}}}), 1) + require.Len(t, dedupWarnings([]client.VertexWarning{{}, {SourceInfo: &pb.SourceInfo{}}}), 2) + }) +} diff --git a/vendor/github.com/mitchellh/hashstructure/v2/LICENSE b/vendor/github.com/mitchellh/hashstructure/v2/LICENSE deleted file mode 100644 index a3866a291fd1..000000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/hashstructure/v2/README.md b/vendor/github.com/mitchellh/hashstructure/v2/README.md deleted file mode 100644 index 21f36be193e1..000000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# hashstructure [![GoDoc](https://godoc.org/github.com/mitchellh/hashstructure?status.svg)](https://godoc.org/github.com/mitchellh/hashstructure) - -hashstructure is a Go library for creating a unique hash value -for arbitrary values in Go. - -This can be used to key values in a hash (for use in a map, set, etc.) -that are complex. The most common use case is comparing two values without -sending data across the network, caching values locally (de-dup), and so on. - -## Features - - * Hash any arbitrary Go value, including complex types. - - * Tag a struct field to ignore it and not affect the hash value. - - * Tag a slice type struct field to treat it as a set where ordering - doesn't affect the hash code but the field itself is still taken into - account to create the hash value. - - * Optionally, specify a custom hash function to optimize for speed, collision - avoidance for your data set, etc. - - * Optionally, hash the output of `.String()` on structs that implement fmt.Stringer, - allowing effective hashing of time.Time - - * Optionally, override the hashing process by implementing `Hashable`. - -## Installation - -Standard `go get`: - -``` -$ go get github.com/mitchellh/hashstructure/v2 -``` - -**Note on v2:** It is highly recommended you use the "v2" release since this -fixes some significant hash collisions issues from v1. In practice, we used -v1 for many years in real projects at HashiCorp and never had issues, but it -is highly dependent on the shape of the data you're hashing and how you use -those hashes. - -When using v2+, you can still generate weaker v1 hashes by using the -`FormatV1` format when calling `Hash`. - -## Usage & Example - -For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure). - -A quick code example is shown below: - -```go -type ComplexStruct struct { - Name string - Age uint - Metadata map[string]interface{} -} - -v := ComplexStruct{ - Name: "mitchellh", - Age: 64, - Metadata: map[string]interface{}{ - "car": true, - "location": "California", - "siblings": []string{"Bob", "John"}, - }, -} - -hash, err := hashstructure.Hash(v, hashstructure.FormatV2, nil) -if err != nil { - panic(err) -} - -fmt.Printf("%d", hash) -// Output: -// 2307517237273902113 -``` diff --git a/vendor/github.com/mitchellh/hashstructure/v2/errors.go b/vendor/github.com/mitchellh/hashstructure/v2/errors.go deleted file mode 100644 index 44b8951478ec..000000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/errors.go +++ /dev/null @@ -1,22 +0,0 @@ -package hashstructure - -import ( - "fmt" -) - -// ErrNotStringer is returned when there's an error with hash:"string" -type ErrNotStringer struct { - Field string -} - -// Error implements error for ErrNotStringer -func (ens *ErrNotStringer) Error() string { - return fmt.Sprintf("hashstructure: %s has hash:\"string\" set, but does not implement fmt.Stringer", ens.Field) -} - -// ErrFormat is returned when an invalid format is given to the Hash function. -type ErrFormat struct{} - -func (*ErrFormat) Error() string { - return "format must be one of the defined Format values in the hashstructure library" -} diff --git a/vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go b/vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go deleted file mode 100644 index 3dc0eb74e07f..000000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go +++ /dev/null @@ -1,482 +0,0 @@ -package hashstructure - -import ( - "encoding/binary" - "fmt" - "hash" - "hash/fnv" - "reflect" - "time" -) - -// HashOptions are options that are available for hashing. -type HashOptions struct { - // Hasher is the hash function to use. If this isn't set, it will - // default to FNV. - Hasher hash.Hash64 - - // TagName is the struct tag to look at when hashing the structure. - // By default this is "hash". - TagName string - - // ZeroNil is flag determining if nil pointer should be treated equal - // to a zero value of pointed type. By default this is false. - ZeroNil bool - - // IgnoreZeroValue is determining if zero value fields should be - // ignored for hash calculation. - IgnoreZeroValue bool - - // SlicesAsSets assumes that a `set` tag is always present for slices. - // Default is false (in which case the tag is used instead) - SlicesAsSets bool - - // UseStringer will attempt to use fmt.Stringer always. If the struct - // doesn't implement fmt.Stringer, it'll fall back to trying usual tricks. - // If this is true, and the "string" tag is also set, the tag takes - // precedence (meaning that if the type doesn't implement fmt.Stringer, we - // panic) - UseStringer bool -} - -// Format specifies the hashing process used. Different formats typically -// generate different hashes for the same value and have different properties. -type Format uint - -const ( - // To disallow the zero value - formatInvalid Format = iota - - // FormatV1 is the format used in v1.x of this library. This has the - // downsides noted in issue #18 but allows simultaneous v1/v2 usage. - FormatV1 - - // FormatV2 is the current recommended format and fixes the issues - // noted in FormatV1. - FormatV2 - - formatMax // so we can easily find the end -) - -// Hash returns the hash value of an arbitrary value. -// -// If opts is nil, then default options will be used. See HashOptions -// for the default values. The same *HashOptions value cannot be used -// concurrently. None of the values within a *HashOptions struct are -// safe to read/write while hashing is being done. -// -// The "format" is required and must be one of the format values defined -// by this library. You should probably just use "FormatV2". This allows -// generated hashes uses alternate logic to maintain compatibility with -// older versions. -// -// Notes on the value: -// -// * Unexported fields on structs are ignored and do not affect the -// hash value. -// -// * Adding an exported field to a struct with the zero value will change -// the hash value. -// -// For structs, the hashing can be controlled using tags. For example: -// -// struct { -// Name string -// UUID string `hash:"ignore"` -// } -// -// The available tag values are: -// -// * "ignore" or "-" - The field will be ignored and not affect the hash code. -// -// * "set" - The field will be treated as a set, where ordering doesn't -// affect the hash code. This only works for slices. -// -// * "string" - The field will be hashed as a string, only works when the -// field implements fmt.Stringer -// -func Hash(v interface{}, format Format, opts *HashOptions) (uint64, error) { - // Validate our format - if format <= formatInvalid || format >= formatMax { - return 0, &ErrFormat{} - } - - // Create default options - if opts == nil { - opts = &HashOptions{} - } - if opts.Hasher == nil { - opts.Hasher = fnv.New64() - } - if opts.TagName == "" { - opts.TagName = "hash" - } - - // Reset the hash - opts.Hasher.Reset() - - // Create our walker and walk the structure - w := &walker{ - format: format, - h: opts.Hasher, - tag: opts.TagName, - zeronil: opts.ZeroNil, - ignorezerovalue: opts.IgnoreZeroValue, - sets: opts.SlicesAsSets, - stringer: opts.UseStringer, - } - return w.visit(reflect.ValueOf(v), nil) -} - -type walker struct { - format Format - h hash.Hash64 - tag string - zeronil bool - ignorezerovalue bool - sets bool - stringer bool -} - -type visitOpts struct { - // Flags are a bitmask of flags to affect behavior of this visit - Flags visitFlag - - // Information about the struct containing this field - Struct interface{} - StructField string -} - -var timeType = reflect.TypeOf(time.Time{}) - -func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) { - t := reflect.TypeOf(0) - - // Loop since these can be wrapped in multiple layers of pointers - // and interfaces. - for { - // If we have an interface, dereference it. We have to do this up - // here because it might be a nil in there and the check below must - // catch that. - if v.Kind() == reflect.Interface { - v = v.Elem() - continue - } - - if v.Kind() == reflect.Ptr { - if w.zeronil { - t = v.Type().Elem() - } - v = reflect.Indirect(v) - continue - } - - break - } - - // If it is nil, treat it like a zero. - if !v.IsValid() { - v = reflect.Zero(t) - } - - // Binary writing can use raw ints, we have to convert to - // a sized-int, we'll choose the largest... - switch v.Kind() { - case reflect.Int: - v = reflect.ValueOf(int64(v.Int())) - case reflect.Uint: - v = reflect.ValueOf(uint64(v.Uint())) - case reflect.Bool: - var tmp int8 - if v.Bool() { - tmp = 1 - } - v = reflect.ValueOf(tmp) - } - - k := v.Kind() - - // We can shortcut numeric values by directly binary writing them - if k >= reflect.Int && k <= reflect.Complex64 { - // A direct hash calculation - w.h.Reset() - err := binary.Write(w.h, binary.LittleEndian, v.Interface()) - return w.h.Sum64(), err - } - - switch v.Type() { - case timeType: - w.h.Reset() - b, err := v.Interface().(time.Time).MarshalBinary() - if err != nil { - return 0, err - } - - err = binary.Write(w.h, binary.LittleEndian, b) - return w.h.Sum64(), err - } - - switch k { - case reflect.Array: - var h uint64 - l := v.Len() - for i := 0; i < l; i++ { - current, err := w.visit(v.Index(i), nil) - if err != nil { - return 0, err - } - - h = hashUpdateOrdered(w.h, h, current) - } - - return h, nil - - case reflect.Map: - var includeMap IncludableMap - if opts != nil && opts.Struct != nil { - if v, ok := opts.Struct.(IncludableMap); ok { - includeMap = v - } - } - - // Build the hash for the map. We do this by XOR-ing all the key - // and value hashes. This makes it deterministic despite ordering. - var h uint64 - for _, k := range v.MapKeys() { - v := v.MapIndex(k) - if includeMap != nil { - incl, err := includeMap.HashIncludeMap( - opts.StructField, k.Interface(), v.Interface()) - if err != nil { - return 0, err - } - if !incl { - continue - } - } - - kh, err := w.visit(k, nil) - if err != nil { - return 0, err - } - vh, err := w.visit(v, nil) - if err != nil { - return 0, err - } - - fieldHash := hashUpdateOrdered(w.h, kh, vh) - h = hashUpdateUnordered(h, fieldHash) - } - - if w.format != FormatV1 { - // Important: read the docs for hashFinishUnordered - h = hashFinishUnordered(w.h, h) - } - - return h, nil - - case reflect.Struct: - parent := v.Interface() - var include Includable - if impl, ok := parent.(Includable); ok { - include = impl - } - - if impl, ok := parent.(Hashable); ok { - return impl.Hash() - } - - // If we can address this value, check if the pointer value - // implements our interfaces and use that if so. - if v.CanAddr() { - vptr := v.Addr() - parentptr := vptr.Interface() - if impl, ok := parentptr.(Includable); ok { - include = impl - } - - if impl, ok := parentptr.(Hashable); ok { - return impl.Hash() - } - } - - t := v.Type() - h, err := w.visit(reflect.ValueOf(t.Name()), nil) - if err != nil { - return 0, err - } - - l := v.NumField() - for i := 0; i < l; i++ { - if innerV := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { - var f visitFlag - fieldType := t.Field(i) - if fieldType.PkgPath != "" { - // Unexported - continue - } - - tag := fieldType.Tag.Get(w.tag) - if tag == "ignore" || tag == "-" { - // Ignore this field - continue - } - - if w.ignorezerovalue { - if innerV.IsZero() { - continue - } - } - - // if string is set, use the string value - if tag == "string" || w.stringer { - if impl, ok := innerV.Interface().(fmt.Stringer); ok { - innerV = reflect.ValueOf(impl.String()) - } else if tag == "string" { - // We only show this error if the tag explicitly - // requests a stringer. - return 0, &ErrNotStringer{ - Field: v.Type().Field(i).Name, - } - } - } - - // Check if we implement includable and check it - if include != nil { - incl, err := include.HashInclude(fieldType.Name, innerV) - if err != nil { - return 0, err - } - if !incl { - continue - } - } - - switch tag { - case "set": - f |= visitFlagSet - } - - kh, err := w.visit(reflect.ValueOf(fieldType.Name), nil) - if err != nil { - return 0, err - } - - vh, err := w.visit(innerV, &visitOpts{ - Flags: f, - Struct: parent, - StructField: fieldType.Name, - }) - if err != nil { - return 0, err - } - - fieldHash := hashUpdateOrdered(w.h, kh, vh) - h = hashUpdateUnordered(h, fieldHash) - } - - if w.format != FormatV1 { - // Important: read the docs for hashFinishUnordered - h = hashFinishUnordered(w.h, h) - } - } - - return h, nil - - case reflect.Slice: - // We have two behaviors here. If it isn't a set, then we just - // visit all the elements. If it is a set, then we do a deterministic - // hash code. - var h uint64 - var set bool - if opts != nil { - set = (opts.Flags & visitFlagSet) != 0 - } - l := v.Len() - for i := 0; i < l; i++ { - current, err := w.visit(v.Index(i), nil) - if err != nil { - return 0, err - } - - if set || w.sets { - h = hashUpdateUnordered(h, current) - } else { - h = hashUpdateOrdered(w.h, h, current) - } - } - - if set && w.format != FormatV1 { - // Important: read the docs for hashFinishUnordered - h = hashFinishUnordered(w.h, h) - } - - return h, nil - - case reflect.String: - // Directly hash - w.h.Reset() - _, err := w.h.Write([]byte(v.String())) - return w.h.Sum64(), err - - default: - return 0, fmt.Errorf("unknown kind to hash: %s", k) - } - -} - -func hashUpdateOrdered(h hash.Hash64, a, b uint64) uint64 { - // For ordered updates, use a real hash function - h.Reset() - - // We just panic if the binary writes fail because we are writing - // an int64 which should never be fail-able. - e1 := binary.Write(h, binary.LittleEndian, a) - e2 := binary.Write(h, binary.LittleEndian, b) - if e1 != nil { - panic(e1) - } - if e2 != nil { - panic(e2) - } - - return h.Sum64() -} - -func hashUpdateUnordered(a, b uint64) uint64 { - return a ^ b -} - -// After mixing a group of unique hashes with hashUpdateUnordered, it's always -// necessary to call hashFinishUnordered. Why? Because hashUpdateUnordered -// is a simple XOR, and calling hashUpdateUnordered on hashes produced by -// hashUpdateUnordered can effectively cancel out a previous change to the hash -// result if the same hash value appears later on. For example, consider: -// -// hashUpdateUnordered(hashUpdateUnordered("A", "B"), hashUpdateUnordered("A", "C")) = -// H("A") ^ H("B")) ^ (H("A") ^ H("C")) = -// (H("A") ^ H("A")) ^ (H("B") ^ H(C)) = -// H(B) ^ H(C) = -// hashUpdateUnordered(hashUpdateUnordered("Z", "B"), hashUpdateUnordered("Z", "C")) -// -// hashFinishUnordered "hardens" the result, so that encountering partially -// overlapping input data later on in a different context won't cancel out. -func hashFinishUnordered(h hash.Hash64, a uint64) uint64 { - h.Reset() - - // We just panic if the writes fail - e1 := binary.Write(h, binary.LittleEndian, a) - if e1 != nil { - panic(e1) - } - - return h.Sum64() -} - -// visitFlag is used as a bitmask for affecting visit behavior -type visitFlag uint - -const ( - visitFlagInvalid visitFlag = iota - visitFlagSet = iota << 1 -) diff --git a/vendor/github.com/mitchellh/hashstructure/v2/include.go b/vendor/github.com/mitchellh/hashstructure/v2/include.go deleted file mode 100644 index 702d35415d4a..000000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/include.go +++ /dev/null @@ -1,22 +0,0 @@ -package hashstructure - -// Includable is an interface that can optionally be implemented by -// a struct. It will be called for each field in the struct to check whether -// it should be included in the hash. -type Includable interface { - HashInclude(field string, v interface{}) (bool, error) -} - -// IncludableMap is an interface that can optionally be implemented by -// a struct. It will be called when a map-type field is found to ask the -// struct if the map item should be included in the hash. -type IncludableMap interface { - HashIncludeMap(field string, k, v interface{}) (bool, error) -} - -// Hashable is an interface that can optionally be implemented by a struct -// to override the hash value. This value will override the hash value for -// the entire struct. Entries in the struct will not be hashed. -type Hashable interface { - Hash() (uint64, error) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3de18c8a4aaa..1b473f434421 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -641,9 +641,6 @@ github.com/mattn/go-shellwords # github.com/mitchellh/go-wordwrap v1.0.1 ## explicit; go 1.14 github.com/mitchellh/go-wordwrap -# github.com/mitchellh/hashstructure/v2 v2.0.2 -## explicit; go 1.14 -github.com/mitchellh/hashstructure/v2 # github.com/moby/buildkit v0.33.0 ## explicit; go 1.26.3 github.com/moby/buildkit/api/services/control