@@ -18,6 +18,7 @@ import (
1818 "bytes"
1919 "fmt"
2020 "reflect"
21+ "sync"
2122
2223 _cbor "github.com/fxamacker/cbor/v2"
2324 "github.com/jinzhu/copier"
@@ -109,27 +110,43 @@ func DecodeById(
109110 return ret , nil
110111}
111112
113+ var decodeGenericTypeCache = map [reflect.Type ]reflect.Type {}
114+ var decodeGenericTypeCacheMutex sync.RWMutex
115+
112116// DecodeGeneric decodes the specified CBOR into the destination object without using the
113117// destination object's UnmarshalCBOR() function
114118func DecodeGeneric (cborData []byte , dest interface {}) error {
115- // Create a duplicate(-ish) struct from the destination
116- // We do this so that we can bypass any custom UnmarshalCBOR() function on the
117- // destination object
119+ // Get destination type
118120 valueDest := reflect .ValueOf (dest )
119- if valueDest .Kind () != reflect .Pointer ||
120- valueDest .Elem ().Kind () != reflect .Struct {
121- return fmt .Errorf ("destination must be a pointer to a struct" )
122- }
123- typeDestElem := valueDest .Elem ().Type ()
124- destTypeFields := []reflect.StructField {}
125- for i := 0 ; i < typeDestElem .NumField (); i ++ {
126- tmpField := typeDestElem .Field (i )
127- if tmpField .IsExported () && tmpField .Name != "DecodeStoreCbor" {
128- destTypeFields = append (destTypeFields , tmpField )
121+ typeDest := valueDest .Elem ().Type ()
122+ // Check type cache
123+ decodeGenericTypeCacheMutex .RLock ()
124+ tmpTypeDest , ok := decodeGenericTypeCache [typeDest ]
125+ decodeGenericTypeCacheMutex .RUnlock ()
126+ if ! ok {
127+ // Create a duplicate(-ish) struct from the destination
128+ // We do this so that we can bypass any custom UnmarshalCBOR() function on the
129+ // destination object
130+ if valueDest .Kind () != reflect .Pointer ||
131+ valueDest .Elem ().Kind () != reflect .Struct {
132+ decodeGenericTypeCacheMutex .Unlock ()
133+ return fmt .Errorf ("destination must be a pointer to a struct" )
134+ }
135+ destTypeFields := []reflect.StructField {}
136+ for i := 0 ; i < typeDest .NumField (); i ++ {
137+ tmpField := typeDest .Field (i )
138+ if tmpField .IsExported () && tmpField .Name != "DecodeStoreCbor" {
139+ destTypeFields = append (destTypeFields , tmpField )
140+ }
129141 }
142+ tmpTypeDest = reflect .StructOf (destTypeFields )
143+ // Populate cache
144+ decodeGenericTypeCacheMutex .Lock ()
145+ decodeGenericTypeCache [typeDest ] = tmpTypeDest
146+ decodeGenericTypeCacheMutex .Unlock ()
130147 }
131148 // Create temporary object with the type created above
132- tmpDest := reflect .New (reflect . StructOf ( destTypeFields ) )
149+ tmpDest := reflect .New (tmpTypeDest )
133150 // Decode CBOR into temporary object
134151 if _ , err := Decode (cborData , tmpDest .Interface ()); err != nil {
135152 return err
0 commit comments