gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/encoding/encoder.go (about)

     1  // This code is based on encoding/json and gorilla/schema
     2  
     3  package encoding
     4  
     5  import (
     6  	"errors"
     7  	"reflect"
     8  	"runtime"
     9  	"sync"
    10  )
    11  
    12  type encoderFunc func(v reflect.Value) (interface{}, error)
    13  
    14  // Encode returns the encoded value of v.
    15  //
    16  // Encode  traverses the value v recursively and looks for structs. If a struct
    17  // is found then it is checked for tagged fields and convert to
    18  // map[string]interface{}
    19  func Encode(v interface{}) (ev interface{}, err error) {
    20  	defer func() {
    21  		if r := recover(); r != nil {
    22  			if _, ok := r.(runtime.Error); ok {
    23  				panic(r)
    24  			}
    25  			if v, ok := r.(string); ok {
    26  				err = errors.New(v)
    27  			} else {
    28  				err = r.(error)
    29  			}
    30  		}
    31  	}()
    32  
    33  	return encode(reflect.ValueOf(v))
    34  }
    35  
    36  func encode(v reflect.Value) (interface{}, error) {
    37  	return valueEncoder(v)(v)
    38  }
    39  
    40  var encoderCache struct {
    41  	sync.RWMutex
    42  	m map[reflect.Type]encoderFunc
    43  }
    44  
    45  func valueEncoder(v reflect.Value) encoderFunc {
    46  	if !v.IsValid() {
    47  		return invalidValueEncoder
    48  	}
    49  	return typeEncoder(v.Type())
    50  }
    51  
    52  func typeEncoder(t reflect.Type) encoderFunc {
    53  	encoderCache.RLock()
    54  	f := encoderCache.m[t]
    55  	encoderCache.RUnlock()
    56  	if f != nil {
    57  		return f
    58  	}
    59  
    60  	// To deal with recursive types, populate the map with an
    61  	// indirect func before we build it. This type waits on the
    62  	// real func (f) to
    63  	//  be ready and then calls it.  This indirect
    64  	// func is only used for recursive types.
    65  	encoderCache.Lock()
    66  	var wg sync.WaitGroup
    67  	wg.Add(1)
    68  	encoderCache.m[t] = func(v reflect.Value) (interface{}, error) {
    69  		wg.Wait()
    70  		return f(v)
    71  	}
    72  	encoderCache.Unlock()
    73  
    74  	// Compute fields without lock.
    75  	// Might duplicate effort but won't hold other computations back.
    76  	f = newTypeEncoder(t, true)
    77  	wg.Done()
    78  	encoderCache.Lock()
    79  	encoderCache.m[t] = f
    80  	encoderCache.Unlock()
    81  	return f
    82  }