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  }