github.com/alexflint/go-memdump@v1.1.0/heterogeneous.go (about) 1 package memdump 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/gob" 7 "fmt" 8 "io" 9 "reflect" 10 ) 11 12 type heterogeneousFooter struct { 13 Pointers []int64 // Pointers contains the offset of each pointer 14 Main int64 // Main contains the offset of the primary object 15 Descriptor descriptor 16 } 17 18 // HeterogeneousEncoder writes memdumps to the provided writer 19 type HeterogeneousEncoder struct { 20 w io.Writer 21 hasprotocol bool 22 } 23 24 // NewHeterogeneousEncoder creates an HeterogeneousEncoder that writes memdumps to the provided writer 25 func NewHeterogeneousEncoder(w io.Writer) *HeterogeneousEncoder { 26 return &HeterogeneousEncoder{ 27 w: w, 28 } 29 } 30 31 // Encode writes a memdump of the provided object to output. You must pass a 32 // pointer to the object you wish to encode. To encode a pointer, pass a 33 // double-pointer. 34 func (e *HeterogeneousEncoder) Encode(obj interface{}) error { 35 t := reflect.TypeOf(obj) 36 if t.Kind() != reflect.Ptr { 37 panic(fmt.Sprintf("expected a pointer but got %T", obj)) 38 } 39 40 // write a protocol heterogeneousProtocol number 41 if !e.hasprotocol { 42 err := binary.Write(e.w, binary.LittleEndian, heterogeneousProtocol) 43 if err != nil { 44 return fmt.Errorf("error writing protocol: %v", err) 45 } 46 e.hasprotocol = true 47 } 48 49 // first segment: write the object data 50 mem := newMemEncoder(e.w) 51 ptrs, err := mem.Encode(obj) 52 if err != nil { 53 return fmt.Errorf("error writing data segment: %v", err) 54 } 55 56 // write delimiter 57 _, err = e.w.Write(delim) 58 if err != nil { 59 return fmt.Errorf("error writing delimiter: %v", err) 60 } 61 62 // second segment: write the metadata 63 gob := gob.NewEncoder(e.w) 64 err = gob.Encode(heterogeneousFooter{ 65 Pointers: ptrs, 66 Descriptor: describe(t.Elem()), 67 }) 68 if err != nil { 69 return fmt.Errorf("error writing heterogeneousFooter: %v", err) 70 } 71 72 // write delimiter 73 _, err = e.w.Write(delim) 74 if err != nil { 75 return fmt.Errorf("error writing delimiter: %v", err) 76 } 77 return nil 78 } 79 80 // HeterogeneousDecoder reads memdumps from the provided reader 81 type HeterogeneousDecoder struct { 82 r io.Reader 83 dr *DelimitedReader 84 hasprotocol bool 85 } 86 87 // NewHeterogeneousDecoder creates a HeterogeneousDecoder that reads memdumps 88 func NewHeterogeneousDecoder(r io.Reader) *HeterogeneousDecoder { 89 return &HeterogeneousDecoder{ 90 r: r, 91 dr: NewDelimitedReader(r), 92 } 93 } 94 95 // Decode reads an object of the specified type from the input. 96 // The object passed to Decode must be a pointer to the type 97 // was originally passed to Encode(). 98 func (d *HeterogeneousDecoder) Decode(dest interface{}) error { 99 t := reflect.TypeOf(dest) 100 if t.Kind() != reflect.Ptr { 101 panic(fmt.Sprintf("expected a pointer but got %T", dest)) 102 } 103 104 ptr, err := d.DecodePtr(t.Elem()) 105 if err != nil { 106 return err 107 } 108 reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(ptr).Elem()) 109 return nil 110 } 111 112 // DecodePtr reads an object of the specified type from the input 113 // and returns a pointer to it. The provided type must be the result 114 // of calling reflect.TypeOf(x) where x is the object originally 115 // passed to Encode(). The return valoue will be of type *x 116 func (d *HeterogeneousDecoder) DecodePtr(typ reflect.Type) (interface{}, error) { 117 // read protocol 118 if !d.hasprotocol { 119 var protocol int32 120 err := binary.Read(d.r, binary.LittleEndian, &protocol) 121 if err != nil { 122 return nil, fmt.Errorf("error reading protocol: %v", err) 123 } 124 if protocol != heterogeneousProtocol { 125 return nil, fmt.Errorf("invalid protocol %d", protocol) 126 } 127 d.hasprotocol = true 128 } 129 130 // first segment: read the memory buffer 131 dataseg, err := d.dr.Next() 132 if len(dataseg) == 0 && err == io.EOF { 133 return nil, io.EOF 134 } 135 if err != nil { 136 return nil, fmt.Errorf("error reading data segment: %v", err) 137 } 138 139 // read the footer 140 footerseg, err := d.dr.Next() 141 if err != nil { 142 return nil, fmt.Errorf("error reading footer segment: %v", err) 143 } 144 145 // decode footer 146 var f heterogeneousFooter 147 dec := gob.NewDecoder(bytes.NewBuffer(footerseg)) 148 err = dec.Decode(&f) 149 if err != nil { 150 return nil, fmt.Errorf("error decoding footer: %v", err) 151 } 152 153 // compare descriptors 154 descr := describe(typ) 155 if !descriptorsEqual(descr, f.Descriptor) { 156 return nil, ErrIncompatibleLayout 157 } 158 159 // relocate the data 160 return relocate(dataseg, f.Pointers, f.Main, typ) 161 }