github.com/alexflint/go-memdump@v1.1.0/descriptor.go (about)

     1  package memdump
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  )
     7  
     8  // pointer - type
     9  // slice - type
    10  // array - type, length
    11  // string
    12  // int - size
    13  // float - size
    14  
    15  // A Descriptor describes a type such that if two types have the
    16  // same descriptor then their memory layout is identical.
    17  type descriptor []typ
    18  
    19  // a class contains information about a type
    20  type typ struct {
    21  	Kind   reflect.Kind // Kind is the kind of this type
    22  	Size   uintptr      // Size is the size in bits, as per reflect.Value.Size
    23  	Elem   int          // Elem is index of the underlying type for pointers, slices, and arrays
    24  	Fields []field      // Fields contains the fields for structs
    25  }
    26  
    27  type field struct {
    28  	Name   string  // Name is the name of this field, or its memdump tag if present
    29  	Offset uintptr // Offset is the position of this field relative to the beginning of the struct
    30  	Type   int     // ID is the index of the type of this field in the descripto
    31  }
    32  
    33  // descriptorsEqual compares two descriptors
    34  func descriptorsEqual(a, b descriptor) bool {
    35  	if len(a) != len(b) {
    36  		return false
    37  	}
    38  	for i := range a {
    39  		if a[i].Kind != b[i].Kind {
    40  			return false
    41  		}
    42  		if a[i].Size != b[i].Size {
    43  			return false
    44  		}
    45  		if a[i].Elem != b[i].Elem {
    46  			return false
    47  		}
    48  		if len(a[i].Fields) != len(b[i].Fields) {
    49  			return false
    50  		}
    51  		for j := range a[i].Fields {
    52  			if a[i].Fields[j].Name != b[i].Fields[j].Name {
    53  				return false
    54  			}
    55  			if a[i].Fields[j].Offset != b[i].Fields[j].Offset {
    56  				return false
    57  			}
    58  			if a[i].Fields[j].Type != b[i].Fields[j].Type {
    59  				return false
    60  			}
    61  		}
    62  	}
    63  	return true
    64  }
    65  
    66  // describe computes the descriptor for a type
    67  func describe(t reflect.Type) descriptor {
    68  	var nextID int
    69  	var desc descriptor
    70  	var queue []reflect.Type
    71  	seen := make(map[reflect.Type]int)
    72  
    73  	push := func(t reflect.Type) int {
    74  		if id, found := seen[t]; found {
    75  			return id
    76  		}
    77  		id := nextID
    78  		seen[t] = id
    79  		nextID++
    80  		queue = append(queue, t)
    81  		return id
    82  	}
    83  
    84  	push(t)
    85  	for len(queue) > 0 {
    86  		cur := queue[0]
    87  		queue = queue[1:]
    88  		t := typ{
    89  			Size: cur.Size(),
    90  			Kind: cur.Kind(),
    91  		}
    92  
    93  		switch cur.Kind() {
    94  		case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map:
    95  			panic(fmt.Sprintf("cannot compute descriptor for %v", cur.Kind()))
    96  		case reflect.Array, reflect.Slice, reflect.Ptr:
    97  			t.Elem = push(cur.Elem())
    98  		case reflect.Struct:
    99  			for i := 0; i < cur.NumField(); i++ {
   100  				f := cur.Field(i)
   101  				if f.Type.Size() == 0 {
   102  					continue
   103  				}
   104  
   105  				name := f.Name
   106  				if tag := f.Tag.Get("memdump"); tag != "" {
   107  					name = tag
   108  				}
   109  				t.Fields = append(t.Fields, field{
   110  					Name:   name,
   111  					Offset: f.Offset,
   112  					Type:   push(f.Type),
   113  				})
   114  			}
   115  		}
   116  
   117  		desc = append(desc, t)
   118  	}
   119  	return desc
   120  }