aboutsummaryrefslogtreecommitdiffstats
path: root/rlp/typecache.go
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2014-11-14 03:31:48 +0800
committerFelix Lange <fjl@twurst.com>2014-11-17 08:49:47 +0800
commit74266d5bbd95ac07882b09230aec876bf7704f69 (patch)
tree101cd83defc7d396f42500bbf080243fb8571acf /rlp/typecache.go
parent9509322ecd8e709ce8a17442836b6ff15ba2edff (diff)
downloadgo-tangerine-74266d5bbd95ac07882b09230aec876bf7704f69.tar.gz
go-tangerine-74266d5bbd95ac07882b09230aec876bf7704f69.tar.zst
go-tangerine-74266d5bbd95ac07882b09230aec876bf7704f69.zip
rlp: new package for streaming RLP decoder
Diffstat (limited to 'rlp/typecache.go')
-rw-r--r--rlp/typecache.go91
1 files changed, 91 insertions, 0 deletions
diff --git a/rlp/typecache.go b/rlp/typecache.go
new file mode 100644
index 000000000..75dbb43c2
--- /dev/null
+++ b/rlp/typecache.go
@@ -0,0 +1,91 @@
+package rlp
+
+import (
+ "fmt"
+ "math/big"
+ "reflect"
+ "sync"
+)
+
+type decoder func(*Stream, reflect.Value) error
+
+type typeinfo struct {
+ decoder
+}
+
+var (
+ typeCacheMutex sync.RWMutex
+ typeCache = make(map[reflect.Type]*typeinfo)
+)
+
+func cachedTypeInfo(typ reflect.Type) (*typeinfo, error) {
+ typeCacheMutex.RLock()
+ info := typeCache[typ]
+ typeCacheMutex.RUnlock()
+ if info != nil {
+ return info, nil
+ }
+ // not in the cache, need to generate info for this type.
+ typeCacheMutex.Lock()
+ defer typeCacheMutex.Unlock()
+ return cachedTypeInfo1(typ)
+}
+
+func cachedTypeInfo1(typ reflect.Type) (*typeinfo, error) {
+ info := typeCache[typ]
+ if info != nil {
+ // another goroutine got the write lock first
+ return info, nil
+ }
+ // put a dummmy value into the cache before generating.
+ // if the generator tries to lookup itself, it will get
+ // the dummy value and won't call itself recursively.
+ typeCache[typ] = new(typeinfo)
+ info, err := genTypeInfo(typ)
+ if err != nil {
+ // remove the dummy value if the generator fails
+ delete(typeCache, typ)
+ return nil, err
+ }
+ *typeCache[typ] = *info
+ return typeCache[typ], err
+}
+
+var (
+ decoderInterface = reflect.TypeOf(new(Decoder)).Elem()
+ bigInt = reflect.TypeOf(big.Int{})
+)
+
+func genTypeInfo(typ reflect.Type) (info *typeinfo, err error) {
+ info = new(typeinfo)
+ kind := typ.Kind()
+ switch {
+ case typ.Implements(decoderInterface):
+ info.decoder = decodeDecoder
+ case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface):
+ info.decoder = decodeDecoderNoPtr
+ case typ.AssignableTo(reflect.PtrTo(bigInt)):
+ info.decoder = decodeBigInt
+ case typ.AssignableTo(bigInt):
+ info.decoder = decodeBigIntNoPtr
+ case isInteger(kind):
+ info.decoder = makeNumDecoder(typ)
+ case kind == reflect.String:
+ info.decoder = decodeString
+ case kind == reflect.Slice || kind == reflect.Array:
+ info.decoder, err = makeListDecoder(typ)
+ case kind == reflect.Struct:
+ info.decoder, err = makeStructDecoder(typ)
+ case kind == reflect.Ptr:
+ info.decoder, err = makePtrDecoder(typ)
+ case kind == reflect.Interface && typ.NumMethod() == 0:
+ info.decoder = decodeInterface
+ default:
+ err = fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
+ }
+ return info, err
+}
+
+func isInteger(k reflect.Kind) bool {
+ return k >= reflect.Int && k <= reflect.Uintptr
+}