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  }