vendor: github.com/google/go-cmp v0.5.7

full diff: https://github.com/google/go-cmp/compare/v0.5.5...v0.5.7

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-03-03 13:31:55 +01:00
parent 12b06fa375
commit 4adea808ce
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
19 changed files with 200 additions and 72 deletions

View File

@ -20,7 +20,7 @@ require (
github.com/fvbommel/sortorder v1.0.2 github.com/fvbommel/sortorder v1.0.2
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.5 github.com/google/go-cmp v0.5.7
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/imdario/mergo v0.3.12 github.com/imdario/mergo v0.3.12
github.com/mitchellh/mapstructure v1.3.2 github.com/mitchellh/mapstructure v1.3.2

View File

@ -358,8 +358,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=

View File

@ -111,7 +111,7 @@ type timeApproximator struct {
func (a timeApproximator) compare(x, y time.Time) bool { func (a timeApproximator) compare(x, y time.Time) bool {
// Avoid subtracting times to avoid overflow when the // Avoid subtracting times to avoid overflow when the
// difference is larger than the largest representible duration. // difference is larger than the largest representable duration.
if x.After(y) { if x.After(y) {
// Ensure x is always before y // Ensure x is always before y
x, y = y, x x, y = y, x

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.13
// +build go1.13 // +build go1.13
package cmpopts package cmpopts

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.13
// +build !go1.13 // +build !go1.13
// TODO(≥go1.13): For support on <go1.13, we use the xerrors package. // TODO(≥go1.13): For support on <go1.13, we use the xerrors package.

View File

@ -36,7 +36,6 @@ import (
"strings" "strings"
"github.com/google/go-cmp/cmp/internal/diff" "github.com/google/go-cmp/cmp/internal/diff"
"github.com/google/go-cmp/cmp/internal/flags"
"github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/function"
"github.com/google/go-cmp/cmp/internal/value" "github.com/google/go-cmp/cmp/internal/value"
) )
@ -319,7 +318,6 @@ func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool {
} }
func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value {
v = sanitizeValue(v, f.Type().In(0))
if !s.dynChecker.Next() { if !s.dynChecker.Next() {
return f.Call([]reflect.Value{v})[0] return f.Call([]reflect.Value{v})[0]
} }
@ -343,8 +341,6 @@ func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value {
} }
func (s *state) callTTBFunc(f, x, y reflect.Value) bool { func (s *state) callTTBFunc(f, x, y reflect.Value) bool {
x = sanitizeValue(x, f.Type().In(0))
y = sanitizeValue(y, f.Type().In(1))
if !s.dynChecker.Next() { if !s.dynChecker.Next() {
return f.Call([]reflect.Value{x, y})[0].Bool() return f.Call([]reflect.Value{x, y})[0].Bool()
} }
@ -372,19 +368,6 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) {
ret = f.Call(vs)[0] ret = f.Call(vs)[0]
} }
// sanitizeValue converts nil interfaces of type T to those of type R,
// assuming that T is assignable to R.
// Otherwise, it returns the input value as is.
func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
// TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/22143).
if !flags.AtLeastGo110 {
if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
return reflect.New(t).Elem()
}
}
return v
}
func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
var addr bool var addr bool
var vax, vay reflect.Value // Addressable versions of vx and vy var vax, vay reflect.Value // Addressable versions of vx and vy

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build purego
// +build purego // +build purego
package cmp package cmp

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego
// +build !purego // +build !purego
package cmp package cmp

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !cmp_debug
// +build !cmp_debug // +build !cmp_debug
package diff package diff

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build cmp_debug
// +build cmp_debug // +build cmp_debug
package diff package diff

View File

@ -1,10 +0,0 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.10
package flags
// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10.
const AtLeastGo110 = false

View File

@ -1,10 +0,0 @@
// Copyright 2019, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.10
package flags
// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10.
const AtLeastGo110 = true

View File

@ -9,6 +9,8 @@ import (
"strconv" "strconv"
) )
var anyType = reflect.TypeOf((*interface{})(nil)).Elem()
// TypeString is nearly identical to reflect.Type.String, // TypeString is nearly identical to reflect.Type.String,
// but has an additional option to specify that full type names be used. // but has an additional option to specify that full type names be used.
func TypeString(t reflect.Type, qualified bool) string { func TypeString(t reflect.Type, qualified bool) string {
@ -20,6 +22,11 @@ func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte
// of the same name and within the same package, // of the same name and within the same package,
// but declared within the namespace of different functions. // but declared within the namespace of different functions.
// Use the "any" alias instead of "interface{}" for better readability.
if t == anyType {
return append(b, "any"...)
}
// Named type. // Named type.
if t.Name() != "" { if t.Name() != "" {
if qualified && t.PkgPath() != "" { if qualified && t.PkgPath() != "" {

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build purego
// +build purego // +build purego
package value package value

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego
// +build !purego // +build !purego
package value package value

View File

@ -178,7 +178,7 @@ type structField struct {
unexported bool unexported bool
mayForce bool // Forcibly allow visibility mayForce bool // Forcibly allow visibility
paddr bool // Was parent addressable? paddr bool // Was parent addressable?
pvx, pvy reflect.Value // Parent values (always addressible) pvx, pvy reflect.Value // Parent values (always addressable)
field reflect.StructField // Field information field reflect.StructField // Field information
} }
@ -315,7 +315,7 @@ func (tf Transform) Option() Option { return tf.trans }
// pops the address from the stack. Thus, when traversing into a pointer from // pops the address from the stack. Thus, when traversing into a pointer from
// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles // reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles
// by checking whether the pointer has already been visited. The cycle detection // by checking whether the pointer has already been visited. The cycle detection
// uses a seperate stack for the x and y values. // uses a separate stack for the x and y values.
// //
// If a cycle is detected we need to determine whether the two pointers // If a cycle is detected we need to determine whether the two pointers
// should be considered equal. The definition of equality chosen by Equal // should be considered equal. The definition of equality chosen by Equal

View File

@ -207,9 +207,10 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
// Check whether this is a []byte of text data. // Check whether this is a []byte of text data.
if t.Elem() == reflect.TypeOf(byte(0)) { if t.Elem() == reflect.TypeOf(byte(0)) {
b := v.Bytes() b := v.Bytes()
isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) && unicode.IsSpace(r) } isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) || unicode.IsSpace(r) }
if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 {
out = opts.formatString("", string(b)) out = opts.formatString("", string(b))
skipType = true
return opts.WithTypeMode(emitType).FormatType(t, out) return opts.WithTypeMode(emitType).FormatType(t, out)
} }
} }

View File

@ -7,6 +7,7 @@ package cmp
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -79,7 +80,7 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
} }
// Use specialized string diffing for longer slices or strings. // Use specialized string diffing for longer slices or strings.
const minLength = 64 const minLength = 32
return vx.Len() >= minLength && vy.Len() >= minLength return vx.Len() >= minLength && vy.Len() >= minLength
} }
@ -96,15 +97,16 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
} }
// Auto-detect the type of the data. // Auto-detect the type of the data.
var isLinedText, isText, isBinary bool
var sx, sy string var sx, sy string
var ssx, ssy []string
var isString, isMostlyText, isPureLinedText, isBinary bool
switch { switch {
case t.Kind() == reflect.String: case t.Kind() == reflect.String:
sx, sy = vx.String(), vy.String() sx, sy = vx.String(), vy.String()
isText = true // Initial estimate, verify later isString = true
case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)): case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)):
sx, sy = string(vx.Bytes()), string(vy.Bytes()) sx, sy = string(vx.Bytes()), string(vy.Bytes())
isBinary = true // Initial estimate, verify later isString = true
case t.Kind() == reflect.Array: case t.Kind() == reflect.Array:
// Arrays need to be addressable for slice operations to work. // Arrays need to be addressable for slice operations to work.
vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem()
@ -112,13 +114,12 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
vy2.Set(vy) vy2.Set(vy)
vx, vy = vx2, vy2 vx, vy = vx2, vy2
} }
if isText || isBinary { if isString {
var numLines, lastLineIdx, maxLineLen int var numTotalRunes, numValidRunes, numLines, lastLineIdx, maxLineLen int
isBinary = !utf8.ValidString(sx) || !utf8.ValidString(sy)
for i, r := range sx + sy { for i, r := range sx + sy {
if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError { numTotalRunes++
isBinary = true if (unicode.IsPrint(r) || unicode.IsSpace(r)) && r != utf8.RuneError {
break numValidRunes++
} }
if r == '\n' { if r == '\n' {
if maxLineLen < i-lastLineIdx { if maxLineLen < i-lastLineIdx {
@ -128,8 +129,26 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
numLines++ numLines++
} }
} }
isText = !isBinary isPureText := numValidRunes == numTotalRunes
isLinedText = isText && numLines >= 4 && maxLineLen <= 1024 isMostlyText = float64(numValidRunes) > math.Floor(0.90*float64(numTotalRunes))
isPureLinedText = isPureText && numLines >= 4 && maxLineLen <= 1024
isBinary = !isMostlyText
// Avoid diffing by lines if it produces a significantly more complex
// edit script than diffing by bytes.
if isPureLinedText {
ssx = strings.Split(sx, "\n")
ssy = strings.Split(sy, "\n")
esLines := diff.Difference(len(ssx), len(ssy), func(ix, iy int) diff.Result {
return diff.BoolResult(ssx[ix] == ssy[iy])
})
esBytes := diff.Difference(len(sx), len(sy), func(ix, iy int) diff.Result {
return diff.BoolResult(sx[ix] == sy[iy])
})
efficiencyLines := float64(esLines.Dist()) / float64(len(esLines))
efficiencyBytes := float64(esBytes.Dist()) / float64(len(esBytes))
isPureLinedText = efficiencyLines < 4*efficiencyBytes
}
} }
// Format the string into printable records. // Format the string into printable records.
@ -138,9 +157,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
switch { switch {
// If the text appears to be multi-lined text, // If the text appears to be multi-lined text,
// then perform differencing across individual lines. // then perform differencing across individual lines.
case isLinedText: case isPureLinedText:
ssx := strings.Split(sx, "\n")
ssy := strings.Split(sy, "\n")
list = opts.formatDiffSlice( list = opts.formatDiffSlice(
reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line",
func(v reflect.Value, d diffMode) textRecord { func(v reflect.Value, d diffMode) textRecord {
@ -229,7 +246,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
// If the text appears to be single-lined text, // If the text appears to be single-lined text,
// then perform differencing in approximately fixed-sized chunks. // then perform differencing in approximately fixed-sized chunks.
// The output is printed as quoted strings. // The output is printed as quoted strings.
case isText: case isMostlyText:
list = opts.formatDiffSlice( list = opts.formatDiffSlice(
reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte",
func(v reflect.Value, d diffMode) textRecord { func(v reflect.Value, d diffMode) textRecord {
@ -237,7 +254,6 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
return textRecord{Diff: d, Value: textLine(s)} return textRecord{Diff: d, Value: textLine(s)}
}, },
) )
delim = ""
// If the text appears to be binary data, // If the text appears to be binary data,
// then perform differencing in approximately fixed-sized chunks. // then perform differencing in approximately fixed-sized chunks.
@ -299,7 +315,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
// Wrap the output with appropriate type information. // Wrap the output with appropriate type information.
var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
if !isText { if !isMostlyText {
// The "{...}" byte-sequence literal is not valid Go syntax for strings. // The "{...}" byte-sequence literal is not valid Go syntax for strings.
// Emit the type for extra clarity (e.g. "string{...}"). // Emit the type for extra clarity (e.g. "string{...}").
if t.Kind() == reflect.String { if t.Kind() == reflect.String {
@ -338,8 +354,11 @@ func (opts formatOptions) formatDiffSlice(
vx, vy reflect.Value, chunkSize int, name string, vx, vy reflect.Value, chunkSize int, name string,
makeRec func(reflect.Value, diffMode) textRecord, makeRec func(reflect.Value, diffMode) textRecord,
) (list textList) { ) (list textList) {
es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result { eq := func(ix, iy int) bool {
return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface()) return vx.Index(ix).Interface() == vy.Index(iy).Interface()
}
es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result {
return diff.BoolResult(eq(ix, iy))
}) })
appendChunks := func(v reflect.Value, d diffMode) int { appendChunks := func(v reflect.Value, d diffMode) int {
@ -364,6 +383,7 @@ func (opts formatOptions) formatDiffSlice(
groups := coalesceAdjacentEdits(name, es) groups := coalesceAdjacentEdits(name, es)
groups = coalesceInterveningIdentical(groups, chunkSize/4) groups = coalesceInterveningIdentical(groups, chunkSize/4)
groups = cleanupSurroundingIdentical(groups, eq)
maxGroup := diffStats{Name: name} maxGroup := diffStats{Name: name}
for i, ds := range groups { for i, ds := range groups {
if maxLen >= 0 && numDiffs >= maxLen { if maxLen >= 0 && numDiffs >= maxLen {
@ -416,25 +436,36 @@ func (opts formatOptions) formatDiffSlice(
// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent // coalesceAdjacentEdits coalesces the list of edits into groups of adjacent
// equal or unequal counts. // equal or unequal counts.
//
// Example:
//
// Input: "..XXY...Y"
// Output: [
// {NumIdentical: 2},
// {NumRemoved: 2, NumInserted 1},
// {NumIdentical: 3},
// {NumInserted: 1},
// ]
//
func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) {
var prevCase int // Arbitrary index into which case last occurred var prevMode byte
lastStats := func(i int) *diffStats { lastStats := func(mode byte) *diffStats {
if prevCase != i { if prevMode != mode {
groups = append(groups, diffStats{Name: name}) groups = append(groups, diffStats{Name: name})
prevCase = i prevMode = mode
} }
return &groups[len(groups)-1] return &groups[len(groups)-1]
} }
for _, e := range es { for _, e := range es {
switch e { switch e {
case diff.Identity: case diff.Identity:
lastStats(1).NumIdentical++ lastStats('=').NumIdentical++
case diff.UniqueX: case diff.UniqueX:
lastStats(2).NumRemoved++ lastStats('!').NumRemoved++
case diff.UniqueY: case diff.UniqueY:
lastStats(2).NumInserted++ lastStats('!').NumInserted++
case diff.Modified: case diff.Modified:
lastStats(2).NumModified++ lastStats('!').NumModified++
} }
} }
return groups return groups
@ -444,6 +475,35 @@ func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats)
// equal groups into adjacent unequal groups that currently result in a // equal groups into adjacent unequal groups that currently result in a
// dual inserted/removed printout. This acts as a high-pass filter to smooth // dual inserted/removed printout. This acts as a high-pass filter to smooth
// out high-frequency changes within the windowSize. // out high-frequency changes within the windowSize.
//
// Example:
//
// WindowSize: 16,
// Input: [
// {NumIdentical: 61}, // group 0
// {NumRemoved: 3, NumInserted: 1}, // group 1
// {NumIdentical: 6}, // ├── coalesce
// {NumInserted: 2}, // ├── coalesce
// {NumIdentical: 1}, // ├── coalesce
// {NumRemoved: 9}, // └── coalesce
// {NumIdentical: 64}, // group 2
// {NumRemoved: 3, NumInserted: 1}, // group 3
// {NumIdentical: 6}, // ├── coalesce
// {NumInserted: 2}, // ├── coalesce
// {NumIdentical: 1}, // ├── coalesce
// {NumRemoved: 7}, // ├── coalesce
// {NumIdentical: 1}, // ├── coalesce
// {NumRemoved: 2}, // └── coalesce
// {NumIdentical: 63}, // group 4
// ]
// Output: [
// {NumIdentical: 61},
// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3},
// {NumIdentical: 64},
// {NumIdentical: 8, NumRemoved: 12, NumInserted: 3},
// {NumIdentical: 63},
// ]
//
func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats {
groups, groupsOrig := groups[:0], groups groups, groupsOrig := groups[:0], groups
for i, ds := range groupsOrig { for i, ds := range groupsOrig {
@ -463,3 +523,91 @@ func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStat
} }
return groups return groups
} }
// cleanupSurroundingIdentical scans through all unequal groups, and
// moves any leading sequence of equal elements to the preceding equal group and
// moves and trailing sequence of equal elements to the succeeding equal group.
//
// This is necessary since coalesceInterveningIdentical may coalesce edit groups
// together such that leading/trailing spans of equal elements becomes possible.
// Note that this can occur even with an optimal diffing algorithm.
//
// Example:
//
// Input: [
// {NumIdentical: 61},
// {NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements
// {NumIdentical: 67},
// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // assume 10 trailing identical elements
// {NumIdentical: 54},
// ]
// Output: [
// {NumIdentical: 64}, // incremented by 3
// {NumRemoved: 9},
// {NumIdentical: 67},
// {NumRemoved: 9},
// {NumIdentical: 64}, // incremented by 10
// ]
//
func cleanupSurroundingIdentical(groups []diffStats, eq func(i, j int) bool) []diffStats {
var ix, iy int // indexes into sequence x and y
for i, ds := range groups {
// Handle equal group.
if ds.NumDiff() == 0 {
ix += ds.NumIdentical
iy += ds.NumIdentical
continue
}
// Handle unequal group.
nx := ds.NumIdentical + ds.NumRemoved + ds.NumModified
ny := ds.NumIdentical + ds.NumInserted + ds.NumModified
var numLeadingIdentical, numTrailingIdentical int
for j := 0; j < nx && j < ny && eq(ix+j, iy+j); j++ {
numLeadingIdentical++
}
for j := 0; j < nx && j < ny && eq(ix+nx-1-j, iy+ny-1-j); j++ {
numTrailingIdentical++
}
if numIdentical := numLeadingIdentical + numTrailingIdentical; numIdentical > 0 {
if numLeadingIdentical > 0 {
// Remove leading identical span from this group and
// insert it into the preceding group.
if i-1 >= 0 {
groups[i-1].NumIdentical += numLeadingIdentical
} else {
// No preceding group exists, so prepend a new group,
// but do so after we finish iterating over all groups.
defer func() {
groups = append([]diffStats{{Name: groups[0].Name, NumIdentical: numLeadingIdentical}}, groups...)
}()
}
// Increment indexes since the preceding group would have handled this.
ix += numLeadingIdentical
iy += numLeadingIdentical
}
if numTrailingIdentical > 0 {
// Remove trailing identical span from this group and
// insert it into the succeeding group.
if i+1 < len(groups) {
groups[i+1].NumIdentical += numTrailingIdentical
} else {
// No succeeding group exists, so append a new group,
// but do so after we finish iterating over all groups.
defer func() {
groups = append(groups, diffStats{Name: groups[len(groups)-1].Name, NumIdentical: numTrailingIdentical})
}()
}
// Do not increment indexes since the succeeding group will handle this.
}
// Update this group since some identical elements were removed.
nx -= numIdentical
ny -= numIdentical
groups[i] = diffStats{Name: ds.Name, NumRemoved: nx, NumInserted: ny}
}
ix += nx
iy += ny
}
return groups
}

2
vendor/modules.txt vendored
View File

@ -119,7 +119,7 @@ github.com/golang/protobuf/ptypes
github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/any
github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/duration
github.com/golang/protobuf/ptypes/timestamp github.com/golang/protobuf/ptypes/timestamp
# github.com/google/go-cmp v0.5.5 # github.com/google/go-cmp v0.5.7
## explicit ## explicit
github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp
github.com/google/go-cmp/cmp/cmpopts github.com/google/go-cmp/cmp/cmpopts