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 }