1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
package litter
import (
"reflect"
"sort"
)
type pointerMap struct {
pointers []uintptr
reusedPointers []uintptr
}
// MapReusedPointers : Given a structure, it will recurively map all pointers mentioned in the tree, breaking
// circular references and provide a list of all pointers that was referenced at least twice
// by the provided structure.
func MapReusedPointers(v reflect.Value) []uintptr {
pm := &pointerMap{
reusedPointers: []uintptr{},
}
pm.consider(v)
return pm.reusedPointers
}
// Recursively consider v and each of its children, updating the map according to the
// semantics of MapReusedPointers
func (pm *pointerMap) consider(v reflect.Value) {
if v.Kind() == reflect.Invalid {
return
}
// fmt.Printf("Considering [%s] %#v\n\r", v.Type().String(), v.Interface())
if isPointerValue(v) && v.Pointer() != 0 { // pointer is 0 for unexported fields
// fmt.Printf("Ptr is %d\n\r", v.Pointer())
reused := pm.addPointerReturnTrueIfWasReused(v.Pointer())
if reused {
// No use descending inside this value, since it have been seen before and all its descendants
// have been considered
return
}
}
// Now descend into any children of this value
switch v.Kind() {
case reflect.Slice, reflect.Array:
numEntries := v.Len()
for i := 0; i < numEntries; i++ {
pm.consider(v.Index(i))
}
case reflect.Interface:
pm.consider(v.Elem())
case reflect.Ptr:
pm.consider(v.Elem())
case reflect.Map:
keys := v.MapKeys()
sort.Sort(mapKeySorter{
keys: keys,
options: &Config,
})
for _, key := range keys {
pm.consider(v.MapIndex(key))
}
case reflect.Struct:
numFields := v.NumField()
for i := 0; i < numFields; i++ {
pm.consider(v.Field(i))
}
}
}
// addPointer to the pointerMap, update reusedPointers. Returns true if pointer was reused
func (pm *pointerMap) addPointerReturnTrueIfWasReused(ptr uintptr) bool {
// Is this allready known to be reused?
for _, have := range pm.reusedPointers {
if ptr == have {
return true
}
}
// Have we seen it once before?
for _, seen := range pm.pointers {
if ptr == seen {
// Add it to the register of pointers we have seen more than once
pm.reusedPointers = append(pm.reusedPointers, ptr)
return true
}
}
// This pointer was new to us
pm.pointers = append(pm.pointers, ptr)
return false
}
|