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 = "⟫"
)
func formatPointer(p value .Pointer , withDelims bool ) string {
v := p .Uintptr ()
if flags .Deterministic {
v = 0xdeadf00f
}
if withDelims {
return pointerDelimPrefix + formatHex (uint64 (v )) + pointerDelimSuffix
}
return formatHex (uint64 (v ))
}
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 ]
}
type trunkReferences struct { pp [2 ]value .Pointer }
type trunkReference struct { p value .Pointer }
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 }}
}
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 )
}
}
}
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 )
}
}
})
if len (leaves ) == 0 {
return
}
leafPtrs := make (map [value .Pointer ]bool )
for _ , leaf := range leaves {
leafPtrs [leaf .Metadata .(leafReference ).p ] = true
}
pairedTrunkPtrs := make (map [value .Pointer ]value .Pointer )
unpair := func (p value .Pointer ) {
if !pairedTrunkPtrs [p ].IsNil () {
pairedTrunkPtrs [pairedTrunkPtrs [p ]] = value .Pointer {}
}
pairedTrunkPtrs [p ] = value .Pointer {}
}
for _ , trunk := range trunks {
switch p := trunk .Metadata .(type ) {
case trunkReference :
unpair (p .p )
case trunkReferences :
p0 , ok0 := pairedTrunkPtrs [p .pp [0 ]]
p1 , ok1 := pairedTrunkPtrs [p .pp [1 ]]
switch {
case !ok0 && !ok1 :
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 ]:
default :
unpair (p .pp [0 ])
unpair (p .pp [1 ])
}
}
}
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 )
if ok0 {
assert (id0 == id1 )
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 ))
}
}
}
}
}
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .