forked from toolshed/abra
		
	
		
			
				
	
	
		
			265 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2020, The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package cmp
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/google/go-cmp/cmp/internal/flags"
 | |
| 	"github.com/google/go-cmp/cmp/internal/value"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	pointerDelimPrefix = "⟪"
 | |
| 	pointerDelimSuffix = "⟫"
 | |
| )
 | |
| 
 | |
| // formatPointer prints the address of the pointer.
 | |
| func formatPointer(p value.Pointer, withDelims bool) string {
 | |
| 	v := p.Uintptr()
 | |
| 	if flags.Deterministic {
 | |
| 		v = 0xdeadf00f // Only used for stable testing purposes
 | |
| 	}
 | |
| 	if withDelims {
 | |
| 		return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix
 | |
| 	}
 | |
| 	return formatHex(uint64(v))
 | |
| }
 | |
| 
 | |
| // pointerReferences is a stack of pointers visited so far.
 | |
| type pointerReferences [][2]value.Pointer
 | |
| 
 | |
| func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) {
 | |
| 	if deref && vx.IsValid() {
 | |
| 		vx = vx.Addr()
 | |
| 	}
 | |
| 	if deref && vy.IsValid() {
 | |
| 		vy = vy.Addr()
 | |
| 	}
 | |
| 	switch d {
 | |
| 	case diffUnknown, diffIdentical:
 | |
| 		pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)}
 | |
| 	case diffRemoved:
 | |
| 		pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}}
 | |
| 	case diffInserted:
 | |
| 		pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)}
 | |
| 	}
 | |
| 	*ps = append(*ps, pp)
 | |
| 	return pp
 | |
| }
 | |
| 
 | |
| func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) {
 | |
| 	p = value.PointerOf(v)
 | |
| 	for _, pp := range *ps {
 | |
| 		if p == pp[0] || p == pp[1] {
 | |
| 			return p, true
 | |
| 		}
 | |
| 	}
 | |
| 	*ps = append(*ps, [2]value.Pointer{p, p})
 | |
| 	return p, false
 | |
| }
 | |
| 
 | |
| func (ps *pointerReferences) Pop() {
 | |
| 	*ps = (*ps)[:len(*ps)-1]
 | |
| }
 | |
| 
 | |
| // trunkReferences is metadata for a textNode indicating that the sub-tree
 | |
| // represents the value for either pointer in a pair of references.
 | |
| type trunkReferences struct{ pp [2]value.Pointer }
 | |
| 
 | |
| // trunkReference is metadata for a textNode indicating that the sub-tree
 | |
| // represents the value for the given pointer reference.
 | |
| type trunkReference struct{ p value.Pointer }
 | |
| 
 | |
| // leafReference is metadata for a textNode indicating that the value is
 | |
| // truncated as it refers to another part of the tree (i.e., a trunk).
 | |
| type leafReference struct{ p value.Pointer }
 | |
| 
 | |
| func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode {
 | |
| 	switch {
 | |
| 	case pp[0].IsNil():
 | |
| 		return &textWrap{Value: s, Metadata: trunkReference{pp[1]}}
 | |
| 	case pp[1].IsNil():
 | |
| 		return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
 | |
| 	case pp[0] == pp[1]:
 | |
| 		return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
 | |
| 	default:
 | |
| 		return &textWrap{Value: s, Metadata: trunkReferences{pp}}
 | |
| 	}
 | |
| }
 | |
| func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode {
 | |
| 	var prefix string
 | |
| 	if printAddress {
 | |
| 		prefix = formatPointer(p, true)
 | |
| 	}
 | |
| 	return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}}
 | |
| }
 | |
| func makeLeafReference(p value.Pointer, printAddress bool) textNode {
 | |
| 	out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"}
 | |
| 	var prefix string
 | |
| 	if printAddress {
 | |
| 		prefix = formatPointer(p, true)
 | |
| 	}
 | |
| 	return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}}
 | |
| }
 | |
| 
 | |
| // resolveReferences walks the textNode tree searching for any leaf reference
 | |
| // metadata and resolves each against the corresponding trunk references.
 | |
| // Since pointer addresses in memory are not particularly readable to the user,
 | |
| // it replaces each pointer value with an arbitrary and unique reference ID.
 | |
| func resolveReferences(s textNode) {
 | |
| 	var walkNodes func(textNode, func(textNode))
 | |
| 	walkNodes = func(s textNode, f func(textNode)) {
 | |
| 		f(s)
 | |
| 		switch s := s.(type) {
 | |
| 		case *textWrap:
 | |
| 			walkNodes(s.Value, f)
 | |
| 		case textList:
 | |
| 			for _, r := range s {
 | |
| 				walkNodes(r.Value, f)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Collect all trunks and leaves with reference metadata.
 | |
| 	var trunks, leaves []*textWrap
 | |
| 	walkNodes(s, func(s textNode) {
 | |
| 		if s, ok := s.(*textWrap); ok {
 | |
| 			switch s.Metadata.(type) {
 | |
| 			case leafReference:
 | |
| 				leaves = append(leaves, s)
 | |
| 			case trunkReference, trunkReferences:
 | |
| 				trunks = append(trunks, s)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	// No leaf references to resolve.
 | |
| 	if len(leaves) == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Collect the set of all leaf references to resolve.
 | |
| 	leafPtrs := make(map[value.Pointer]bool)
 | |
| 	for _, leaf := range leaves {
 | |
| 		leafPtrs[leaf.Metadata.(leafReference).p] = true
 | |
| 	}
 | |
| 
 | |
| 	// Collect the set of trunk pointers that are always paired together.
 | |
| 	// This allows us to assign a single ID to both pointers for brevity.
 | |
| 	// If a pointer in a pair ever occurs by itself or as a different pair,
 | |
| 	// then the pair is broken.
 | |
| 	pairedTrunkPtrs := make(map[value.Pointer]value.Pointer)
 | |
| 	unpair := func(p value.Pointer) {
 | |
| 		if !pairedTrunkPtrs[p].IsNil() {
 | |
| 			pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half
 | |
| 		}
 | |
| 		pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half
 | |
| 	}
 | |
| 	for _, trunk := range trunks {
 | |
| 		switch p := trunk.Metadata.(type) {
 | |
| 		case trunkReference:
 | |
| 			unpair(p.p) // standalone pointer cannot be part of a pair
 | |
| 		case trunkReferences:
 | |
| 			p0, ok0 := pairedTrunkPtrs[p.pp[0]]
 | |
| 			p1, ok1 := pairedTrunkPtrs[p.pp[1]]
 | |
| 			switch {
 | |
| 			case !ok0 && !ok1:
 | |
| 				// Register the newly seen pair.
 | |
| 				pairedTrunkPtrs[p.pp[0]] = p.pp[1]
 | |
| 				pairedTrunkPtrs[p.pp[1]] = p.pp[0]
 | |
| 			case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]:
 | |
| 				// Exact pair already seen; do nothing.
 | |
| 			default:
 | |
| 				// Pair conflicts with some other pair; break all pairs.
 | |
| 				unpair(p.pp[0])
 | |
| 				unpair(p.pp[1])
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Correlate each pointer referenced by leaves to a unique identifier,
 | |
| 	// and print the IDs for each trunk that matches those pointers.
 | |
| 	var nextID uint
 | |
| 	ptrIDs := make(map[value.Pointer]uint)
 | |
| 	newID := func() uint {
 | |
| 		id := nextID
 | |
| 		nextID++
 | |
| 		return id
 | |
| 	}
 | |
| 	for _, trunk := range trunks {
 | |
| 		switch p := trunk.Metadata.(type) {
 | |
| 		case trunkReference:
 | |
| 			if print := leafPtrs[p.p]; print {
 | |
| 				id, ok := ptrIDs[p.p]
 | |
| 				if !ok {
 | |
| 					id = newID()
 | |
| 					ptrIDs[p.p] = id
 | |
| 				}
 | |
| 				trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
 | |
| 			}
 | |
| 		case trunkReferences:
 | |
| 			print0 := leafPtrs[p.pp[0]]
 | |
| 			print1 := leafPtrs[p.pp[1]]
 | |
| 			if print0 || print1 {
 | |
| 				id0, ok0 := ptrIDs[p.pp[0]]
 | |
| 				id1, ok1 := ptrIDs[p.pp[1]]
 | |
| 				isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0]
 | |
| 				if isPair {
 | |
| 					var id uint
 | |
| 					assert(ok0 == ok1) // must be seen together or not at all
 | |
| 					if ok0 {
 | |
| 						assert(id0 == id1) // must have the same ID
 | |
| 						id = id0
 | |
| 					} else {
 | |
| 						id = newID()
 | |
| 						ptrIDs[p.pp[0]] = id
 | |
| 						ptrIDs[p.pp[1]] = id
 | |
| 					}
 | |
| 					trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
 | |
| 				} else {
 | |
| 					if print0 && !ok0 {
 | |
| 						id0 = newID()
 | |
| 						ptrIDs[p.pp[0]] = id0
 | |
| 					}
 | |
| 					if print1 && !ok1 {
 | |
| 						id1 = newID()
 | |
| 						ptrIDs[p.pp[1]] = id1
 | |
| 					}
 | |
| 					switch {
 | |
| 					case print0 && print1:
 | |
| 						trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1))
 | |
| 					case print0:
 | |
| 						trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0))
 | |
| 					case print1:
 | |
| 						trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1))
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Update all leaf references with the unique identifier.
 | |
| 	for _, leaf := range leaves {
 | |
| 		if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok {
 | |
| 			leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id))
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func formatReference(id uint) string {
 | |
| 	return fmt.Sprintf("ref#%d", id)
 | |
| }
 | |
| 
 | |
| func updateReferencePrefix(prefix, ref string) string {
 | |
| 	if prefix == "" {
 | |
| 		return pointerDelimPrefix + ref + pointerDelimSuffix
 | |
| 	}
 | |
| 	suffix := strings.TrimPrefix(prefix, pointerDelimPrefix)
 | |
| 	return pointerDelimPrefix + ref + ": " + suffix
 | |
| }
 |