github.com/alexflint/go-memdump@v1.1.0/homogenous.go (about) 1 package memdump 2 3 import ( 4 "bytes" 5 "encoding/gob" 6 "fmt" 7 "io" 8 "reflect" 9 ) 10 11 // header is gob-encoded in the first segment 12 type header struct { 13 Protocol int32 14 Descriptor descriptor 15 } 16 17 // Encoder writes memdumps to the provided writer 18 type Encoder struct { 19 w io.Writer 20 t reflect.Type 21 } 22 23 // NewEncoder creates an Encoder that writes memdumps to the provided writer. 24 // Each object passed to Encode must be of the same type. 25 func NewEncoder(w io.Writer) *Encoder { 26 return &Encoder{ 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 // pointer to a pointer.) 34 func (e *Encoder) 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 if e.t != nil && e.t != t { 40 panic(fmt.Sprintf("each call to Encode should pass the same type, but got %v then %v", e.t, t)) 41 } 42 43 if e.t == nil { 44 // write the header 45 gob := gob.NewEncoder(e.w) 46 err := gob.Encode(header{ 47 Protocol: homogeneousProtocol, 48 Descriptor: describe(t.Elem()), 49 }) 50 if err != nil { 51 return fmt.Errorf("error writing footer: %v", err) 52 } 53 54 e.t = t 55 _, err = e.w.Write(delim) 56 if err != nil { 57 return fmt.Errorf("error writing delimeter: %v", err) 58 } 59 } 60 61 // first segment: write the object data 62 mem := newMemEncoder(e.w) 63 ptrs, err := mem.Encode(obj) 64 if err != nil { 65 return fmt.Errorf("error writing data segment: %v", err) 66 } 67 68 // write delimiter 69 _, err = e.w.Write(delim) 70 if err != nil { 71 return fmt.Errorf("error writing delimiter: %v", err) 72 } 73 74 // second segment: write the footer 75 err = encodeLocations(e.w, &locations{Pointers: ptrs}) 76 if err != nil { 77 return fmt.Errorf("error writing footer: %v", err) 78 } 79 80 // write delimiter 81 _, err = e.w.Write(delim) 82 if err != nil { 83 return fmt.Errorf("error writing delimiter: %v", err) 84 } 85 return nil 86 } 87 88 // Decoder reads memdumps from the provided reader 89 type Decoder struct { 90 dr *DelimitedReader 91 t reflect.Type 92 } 93 94 // NewDecoder creates a Decoder that reads memdumps 95 func NewDecoder(r io.Reader) *Decoder { 96 return &Decoder{ 97 dr: NewDelimitedReader(r), 98 } 99 } 100 101 // Decode reads an object of the specified type from the input. 102 // The object passed to Decode must be a pointer to the type 103 // was originally passed to Encode(). 104 func (d *Decoder) Decode(dest interface{}) error { 105 t := reflect.TypeOf(dest) 106 if t.Kind() != reflect.Ptr { 107 panic(fmt.Sprintf("expected a pointer but got %T", dest)) 108 } 109 110 ptr, err := d.DecodePtr(t.Elem()) 111 if err != nil { 112 return err 113 } 114 reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(ptr).Elem()) 115 return nil 116 } 117 118 // DecodePtr reads an object of the specified type from the input 119 // and returns a pointer to it. The provided type must be the result 120 // of calling reflect.TypeOf(x) where x is the object originally 121 // passed to Encode(). The return valoue will be of type *x 122 func (d *Decoder) DecodePtr(t reflect.Type) (interface{}, error) { 123 if d.t != nil && d.t != t { 124 panic(fmt.Sprintf("each call to Encode should pass the same type, but got %v then %v", d.t, t)) 125 } 126 127 // read the header 128 if d.t == nil { 129 // decode the descriptor 130 seg, err := d.dr.Next() 131 if err != nil { 132 return nil, fmt.Errorf("error reading header segment: %v", err) 133 } 134 135 var header header 136 dec := gob.NewDecoder(bytes.NewBuffer(seg)) 137 err = dec.Decode(&header) 138 if err != nil { 139 return nil, fmt.Errorf("error decoding header: %v", err) 140 } 141 142 // compare descriptors 143 expectedDescr := describe(t) 144 if !descriptorsEqual(expectedDescr, header.Descriptor) { 145 return nil, ErrIncompatibleLayout 146 } 147 148 d.t = t 149 } 150 151 // read the data 152 dataseg, err := d.dr.Next() 153 if len(dataseg) == 0 && err == io.EOF { 154 return nil, io.EOF 155 } 156 if err != nil { 157 return nil, fmt.Errorf("error reading data segment: %v", err) 158 } 159 160 // read the footer 161 footerseg, err := d.dr.Next() 162 if err != nil { 163 return nil, fmt.Errorf("error decoding footer: %v", err) 164 } 165 166 // decode footer 167 var f locations 168 err = decodeLocations(bytes.NewBuffer(footerseg), &f) 169 if err != nil { 170 return nil, fmt.Errorf("error decoding footer: %v", err) 171 } 172 173 // relocate the data 174 return relocate(dataseg, f.Pointers, f.Main, t) 175 }