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.md file.

package cmp

import (
	
	
	
	
	
	
	

	
)
CanFormatDiffSlice reports whether we support custom formatting for nodes that are slices of primitive kinds or strings.
func ( formatOptions) ( *valueNode) bool {
	switch {
	case .DiffMode != diffUnknown:
		return false // Must be formatting in diff mode
	case .NumDiff == 0:
		return false // No differences detected
	case !.ValueX.IsValid() || !.ValueY.IsValid():
		return false // Both values must be valid
	case .Type.Kind() == reflect.Slice && (.ValueX.Len() == 0 || .ValueY.Len() == 0):
		return false // Both slice values have to be non-empty
	case .NumIgnored > 0:
		return false // Some ignore option was used
	case .NumTransformed > 0:
		return false // Some transform option was used
	case .NumCompared > 1:
		return false // More than one comparison was used
The need for cmp to check applicability of options on every element in a slice is a significant performance detriment for large []byte. The workaround is to specify Comparer(bytes.Equal), which enables cmp to compare []byte more efficiently. If they differ, we still want to provide batched diffing. The logic disallows named types since they tend to have their own String method, with nicer formatting than what this provides.
		return false
	}

	switch  := .Type; .Kind() {
	case reflect.String:
Only slices of primitive types have specialized handling.
If a sufficient number of elements already differ, use specialized formatting even if length requirement is not met.
		if .NumDiff > .NumSame {
			return true
		}
	default:
		return false
	}
Use specialized string diffing for longer slices or strings.
	const  = 64
	return .ValueX.Len() >=  && .ValueY.Len() >= 
}
FormatDiffSlice prints a diff for the slices (or strings) represented by v. This provides custom-tailored logic to make printing of differences in textual strings and slices of primitive kinds more readable.
func ( formatOptions) ( *valueNode) textNode {
	assert(.DiffMode == diffUnknown)
	, ,  := .Type, .ValueX, .ValueY
Auto-detect the type of the data.
	var , ,  bool
	var ,  string
	switch {
	case .Kind() == reflect.String:
		,  = .String(), .String()
		 = true // Initial estimate, verify later
	case .Kind() == reflect.Slice && .Elem() == reflect.TypeOf(byte(0)):
		,  = string(.Bytes()), string(.Bytes())
		 = true // Initial estimate, verify later
Arrays need to be addressable for slice operations to work.
		,  := reflect.New().Elem(), reflect.New().Elem()
		.Set()
		.Set()
		,  = , 
	}
	if  ||  {
		var , ,  int
		 = !utf8.ValidString() || !utf8.ValidString()
		for ,  := range  +  {
			if !(unicode.IsPrint() || unicode.IsSpace()) ||  == utf8.RuneError {
				 = true
				break
			}
			if  == '\n' {
				if  < - {
					 =  - 
				}
				 =  + 1
				++
			}
		}
		 = !
		 =  &&  >= 4 &&  <= 1024
	}
Format the string into printable records.
	var  textList
	var  string
If the text appears to be multi-lined text, then perform differencing across individual lines.
	case :
		 := strings.Split(, "\n")
		 := strings.Split(, "\n")
		 = .formatDiffSlice(
			reflect.ValueOf(), reflect.ValueOf(), 1, "line",
			func( reflect.Value,  diffMode) textRecord {
				 := formatString(.Index(0).String())
				return textRecord{Diff: , Value: textLine()}
			},
		)
		 = "\n"
If possible, use a custom triple-quote (""") syntax for printing differences in a string literal. This format is more readable, but has edge-cases where differences are visually indistinguishable. This format is avoided under the following conditions: • A line starts with `"""` • A line starts with "..." • A line contains non-printable characters • Adjacent different lines differ only by whitespace For example: """ ... // 3 identical lines foo bar - baz + BAZ """
		 := true
		 := map[string]bool{}
		 := map[string]bool{}
		var  textList
		 = append(, textRecord{Value: textLine(`"""`), ElideComma: true})
		for ,  := range  {
			if !.Value.Equal(textEllipsis) {
				,  := strconv.Unquote(string(.Value.(textLine)))
				 = strings.TrimPrefix(strings.TrimSuffix(, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
				 := strings.Map(func( rune) rune {
					if unicode.IsSpace() {
						return -1 // drop whitespace to avoid visually indistinguishable output
					}
					return 
				}, )
				 := func( rune) bool {
					return unicode.IsPrint() ||  == '\t' // specially treat tab as printable
				}
				 = !strings.HasPrefix(, `"""`) && !strings.HasPrefix(, "...") && strings.TrimFunc(, ) == ""
				switch .Diff {
				case diffRemoved:
					 =  && ![]
					[] = true
				case diffInserted:
					 =  && ![]
					[] = true
				}
				if ! {
					break
				}
				.Value = textLine()
				.ElideComma = true
			}
			if !(.Diff == diffRemoved || .Diff == diffInserted) { // start a new non-adjacent difference group
				 = map[string]bool{}
				 = map[string]bool{}
			}
			 = append(, )
		}
		if  := [len()-1]; .Diff == diffIdentical && len(.Value.(textLine)) == 0 {
			 = [:len()-1] // elide single empty line at the end
		}
		 = append(, textRecord{Value: textLine(`"""`), ElideComma: true})
		if  {
			var  textNode = &textWrap{Prefix: "(", Value: , Suffix: ")"}
			switch .Kind() {
			case reflect.String:
				if  != reflect.TypeOf(string("")) {
					 = .FormatType(, )
				}
Always emit type for slices since the triple-quote syntax looks like a string (not a slice).
				 = .WithTypeMode(emitType)
				 = .FormatType(, )
			}
			return 
		}
If the text appears to be single-lined text, then perform differencing in approximately fixed-sized chunks. The output is printed as quoted strings.
	case :
		 = .formatDiffSlice(
			reflect.ValueOf(), reflect.ValueOf(), 64, "byte",
			func( reflect.Value,  diffMode) textRecord {
				 := formatString(.String())
				return textRecord{Diff: , Value: textLine()}
			},
		)
		 = ""
If the text appears to be binary data, then perform differencing in approximately fixed-sized chunks. The output is inspired by hexdump.
	case :
		 = .formatDiffSlice(
			reflect.ValueOf(), reflect.ValueOf(), 16, "byte",
			func( reflect.Value,  diffMode) textRecord {
				var  []string
				for  := 0;  < .Len(); ++ {
					 = append(, formatHex(.Index().Uint()))
				}
				 := strings.Join(, ", ")
				 := commentString(fmt.Sprintf("%c|%v|", , formatASCII(.String())))
				return textRecord{Diff: , Value: textLine(), Comment: }
			},
		)
For all other slices of primitive types, then perform differencing in approximately fixed-sized chunks. The size of each chunk depends on the width of the element kind.
	default:
		var  int
		if .Elem().Kind() == reflect.Bool {
			 = 16
		} else {
			switch .Elem().Bits() {
			case 8:
				 = 16
			case 16:
				 = 12
			case 32:
				 = 8
			default:
				 = 8
			}
		}
		 = .formatDiffSlice(
			, , , .Elem().Kind().String(),
			func( reflect.Value,  diffMode) textRecord {
				var  []string
				for  := 0;  < .Len(); ++ {
					switch .Elem().Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						 = append(, fmt.Sprint(.Index().Int()))
					case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						 = append(, fmt.Sprint(.Index().Uint()))
					case reflect.Uint8, reflect.Uintptr:
						 = append(, formatHex(.Index().Uint()))
					case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
						 = append(, fmt.Sprint(.Index().Interface()))
					}
				}
				 := strings.Join(, ", ")
				return textRecord{Diff: , Value: textLine()}
			},
		)
	}
Wrap the output with appropriate type information.
	var  textNode = &textWrap{Prefix: "{", Value: , Suffix: "}"}
The "{...}" byte-sequence literal is not valid Go syntax for strings. Emit the type for extra clarity (e.g. "string{...}").
		if .Kind() == reflect.String {
			 = .WithTypeMode(emitType)
		}
		return .FormatType(, )
	}
	switch .Kind() {
	case reflect.String:
		 = &textWrap{Prefix: "strings.Join(", Value: , Suffix: fmt.Sprintf(", %q)", )}
		if  != reflect.TypeOf(string("")) {
			 = .FormatType(, )
		}
	case reflect.Slice:
		 = &textWrap{Prefix: "bytes.Join(", Value: , Suffix: fmt.Sprintf(", %q)", )}
		if  != reflect.TypeOf([]byte(nil)) {
			 = .FormatType(, )
		}
	}
	return 
}
formatASCII formats s as an ASCII string. This is useful for printing binary strings in a semi-legible way.
func ( string) string {
	 := bytes.Repeat([]byte{'.'}, len())
	for  := 0;  < len(); ++ {
		if ' ' <= [] && [] <= '~' {
			[] = []
		}
	}
	return string()
}

func ( formatOptions) (
	,  reflect.Value,  int,  string,
	 func(reflect.Value, diffMode) textRecord,
) ( textList) {
	 := diff.Difference(.Len(), .Len(), func( int,  int) diff.Result {
		return diff.BoolResult(.Index().Interface() == .Index().Interface())
	})

	 := func( reflect.Value,  diffMode) int {
		 := .Len()
		for .Len() > 0 {
			 := 
			if  > .Len() {
				 = .Len()
			}
			 = append(, (.Slice(0, ), ))
			 = .Slice(, .Len())
		}
		return  - .Len()
	}

	var  int
	 := -1
	if .LimitVerbosity {
		 = (1 << .verbosity()) << 2 // 4, 8, 16, 32, 64, etc...
		.VerbosityLevel--
	}

	 := coalesceAdjacentEdits(, )
	 = coalesceInterveningIdentical(, /4)
	 := diffStats{Name: }
	for ,  := range  {
		if  >= 0 &&  >=  {
			 = .Append()
			continue
		}
Print equal.
Compute the number of leading and trailing equal bytes to print.
			var ,  int
			 := .NumIgnored + .NumIdentical
			for  < *numContextRecords && + <  &&  != 0 {
				++
			}
			for  < *numContextRecords && + <  &&  != len()-1 {
				++
			}
			if -(+) <=  && .NumIgnored == 0 {
				 =  -  // Avoid pointless coalescing of single equal row
			}
Print the equal bytes.
			(.Slice(0, ), diffIdentical)
			if  > + {
				.NumIdentical -=  + 
				.AppendEllipsis()
			}
			(.Slice(-, ), diffIdentical)
			 = .Slice(, .Len())
			 = .Slice(, .Len())
			continue
		}
Print unequal.
		 := len()
		 := (.Slice(0, .NumIdentical+.NumRemoved+.NumModified), diffRemoved)
		 = .Slice(, .Len())
		 := (.Slice(0, .NumIdentical+.NumInserted+.NumModified), diffInserted)
		 = .Slice(, .Len())
		 += len() - 
	}
	if .IsZero() {
		assert(.Len() == 0 && .Len() == 0)
	} else {
		.AppendEllipsis()
	}
	return 
}
coalesceAdjacentEdits coalesces the list of edits into groups of adjacent equal or unequal counts.
func ( string,  diff.EditScript) ( []diffStats) {
	var  int // Arbitrary index into which case last occurred
	 := func( int) *diffStats {
		if  !=  {
			 = append(, diffStats{Name: })
			 = 
		}
		return &[len()-1]
	}
	for ,  := range  {
		switch  {
		case diff.Identity:
			(1).NumIdentical++
		case diff.UniqueX:
			(2).NumRemoved++
		case diff.UniqueY:
			(2).NumInserted++
		case diff.Modified:
			(2).NumModified++
		}
	}
	return 
}
coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) equal groups into adjacent unequal groups that currently result in a dual inserted/removed printout. This acts as a high-pass filter to smooth out high-frequency changes within the windowSize.
func ( []diffStats,  int) []diffStats {
	,  := [:0], 
	for ,  := range  {
		if len() >= 2 && .NumDiff() > 0 {
			 := &[len()-2] // Unequal group
			 := &[len()-1] // Equal group
			 := &[]         // Unequal group
			,  := .NumRemoved > 0, .NumInserted > 0
			,  := .NumRemoved > 0, .NumInserted > 0
			if (( || ) && ( || )) && .NumIdentical <=  {
				* = .Append(*).Append(*)
				 = [:len()-1] // Truncate off equal group
				continue
			}
		}
		 = append(, )
	}
	return