Convert other pointers to ordinals

This commit is contained in:
James Craig Burley 2019-12-07 10:53:14 -05:00
parent ba57249c50
commit 463de252e3
3 changed files with 70 additions and 37 deletions

View File

@ -216,9 +216,9 @@ func printHexPtr(w io.Writer, p uintptr) {
w.Write(buf) w.Write(buf)
} }
func printOrdinal(w io.Writer, ord uint) { func printOrdinal(w io.Writer, ord uintptr) {
buf := make([]byte, 18) buf := make([]byte, 18)
base := uint(10) base := uintptr(10)
i := len(buf) - 1 i := len(buf) - 1
for ord >= base { for ord >= base {
buf[i] = hexDigits[ord%base] buf[i] = hexDigits[ord%base]

View File

@ -52,11 +52,11 @@ type dumpState struct {
w io.Writer w io.Writer
depth int depth int
pointers map[uintptr]int pointers map[uintptr]int
allPointers map[uintptr]uint allPointers map[uintptr]uintptr
ignoreNextType bool ignoreNextType bool
ignoreNextIndent bool ignoreNextIndent bool
cs *ConfigState cs *ConfigState
nextOrdinal uint nextOrdinal uintptr
} }
// indent performs indentation according to the depth level and cs.Indent // indent performs indentation according to the depth level and cs.Indent
@ -79,6 +79,50 @@ func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
return v return v
} }
func (d *dumpState) isPointerSeen(addr uintptr) (seen bool, ordinal uintptr) {
ordinal = uintptr(addr)
if d.allPointers == nil {
return false, ordinal
}
// d.cs.NoDuplicates || d.cs.UseOrdinals
duplicateFound := false
if ord, ok := d.allPointers[addr]; ok {
if d.cs.UseOrdinals {
ordinal = ord
}
if d.cs.NoDuplicates {
duplicateFound = true
}
} else {
if d.cs.UseOrdinals {
d.nextOrdinal++
ordinal = d.nextOrdinal
}
d.allPointers[addr] = ordinal
}
return duplicateFound, ordinal
}
func (d *dumpState) printPtrOrOrdinal(addr uintptr) {
if d.cs.UseOrdinals {
printOrdinal(d.w, addr)
} else {
printHexPtr(d.w, addr)
}
}
func (d *dumpState) printPtr(addr uintptr) {
if d.cs.UseOrdinals {
_, addr := d.isPointerSeen(addr)
printOrdinal(d.w, addr)
} else {
printHexPtr(d.w, addr)
}
}
// dumpPtr handles formatting of pointers by indirecting them as necessary. // dumpPtr handles formatting of pointers by indirecting them as necessary.
func (d *dumpState) dumpPtr(v reflect.Value) { func (d *dumpState) dumpPtr(v reflect.Value) {
// Remove pointers at or below the current depth from map used to detect // Remove pointers at or below the current depth from map used to detect
@ -99,7 +143,6 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
cycleFound := false cycleFound := false
duplicateFound := false duplicateFound := false
indirects := 0 indirects := 0
ordinal := uint(0)
ve := v ve := v
for ve.Kind() == reflect.Ptr { for ve.Kind() == reflect.Ptr {
if ve.IsNil() { if ve.IsNil() {
@ -113,31 +156,13 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
cycleFound = true cycleFound = true
} }
if d.allPointers != nil { // d.cs.NoDuplicates || d.cs.UseOrdinals dup, ordinal := d.isPointerSeen(addr)
if ord, ok := d.allPointers[addr]; ok {
if d.cs.UseOrdinals {
ordinal = ord
}
if d.cs.NoDuplicates {
duplicateFound = true
}
} else {
if d.cs.UseOrdinals {
d.nextOrdinal++
ordinal = d.nextOrdinal
}
d.allPointers[addr] = ordinal
}
}
if ordinal > 0 { pointerChain = append(pointerChain, ordinal)
pointerChain = append(pointerChain, uintptr(ordinal))
} else {
pointerChain = append(pointerChain, addr)
}
if cycleFound || duplicateFound { if cycleFound || dup {
indirects-- indirects--
duplicateFound = true
break break
} }
@ -166,11 +191,7 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
if i > 0 { if i > 0 {
d.w.Write(pointerChainBytes) d.w.Write(pointerChainBytes)
} }
if d.cs.UseOrdinals { d.printPtrOrOrdinal(addr)
printOrdinal(d.w, uint(addr))
} else {
printHexPtr(d.w, addr)
}
} }
d.w.Write(closeParenBytes) d.w.Write(closeParenBytes)
} }
@ -469,10 +490,10 @@ func (d *dumpState) dump(v reflect.Value) {
d.w.Write(closeBraceBytes) d.w.Write(closeBraceBytes)
case reflect.Uintptr: case reflect.Uintptr:
printHexPtr(d.w, uintptr(v.Uint())) d.printPtr(uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func: case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(d.w, v.Pointer()) d.printPtr(v.Pointer())
// There were not any other types at the time this code was written, but // There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it in case any new // fall back to letting the default fmt package handle it in case any new
@ -501,7 +522,7 @@ func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
d := dumpState{w: w, cs: cs} d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int) d.pointers = make(map[uintptr]int)
if cs.NoDuplicates || cs.UseOrdinals { if cs.NoDuplicates || cs.UseOrdinals {
d.allPointers = make(map[uintptr]uint) d.allPointers = make(map[uintptr]uintptr)
} }
d.dump(reflect.ValueOf(arg)) d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes) d.w.Write(newlineBytes)

View File

@ -1111,6 +1111,12 @@ b: (int) 2
} }
} }
func a() {
}
func b() {
}
func TestDumpDuplicateOrdinals(t *testing.T) { func TestDumpDuplicateOrdinals(t *testing.T) {
cfg := spew.ConfigState{NoDuplicates: true, UseOrdinals: true} cfg := spew.ConfigState{NoDuplicates: true, UseOrdinals: true}
type info struct { type info struct {
@ -1121,9 +1127,12 @@ func TestDumpDuplicateOrdinals(t *testing.T) {
info1 *info info1 *info
info2 *info info2 *info
info3 *info info3 *info
fn1 func()
fn2 func()
fn3 func()
} }
i := &info{a: 1, b: 2} i := &info{a: 1, b: 2}
v := twice{info1: i, info2: &info{a: 3, b: 4}, info3: i} v := twice{info1: i, info2: &info{a: 3, b: 4}, info3: i, fn1: a, fn2: b, fn3: a}
// vt := "spew_test.twice" // vt := "spew_test.twice"
s := cfg.Sdump(v) s := cfg.Sdump(v)
expected := `(spew_test.twice) { expected := `(spew_test.twice) {
@ -1135,7 +1144,10 @@ info2: (*spew_test.info)(#2)({
a: (int) 3, a: (int) 3,
b: (int) 4 b: (int) 4
}), }),
info3: (*spew_test.info)(#1)(<already seen>) info3: (*spew_test.info)(#1)(<already seen>),
fn1: (func()) #3,
fn2: (func()) #4,
fn3: (func()) #3
} }
` `
if s != expected { if s != expected {