github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/rlp/typecache.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package rlp
    18  
    19  import (
    20  	"reflect"
    21  	"sync"
    22  )
    23  
    24  var (
    25  	typeCacheMutex sync.RWMutex
    26  	typeCache      = make(map[typekey]*typeinfo)
    27  )
    28  
    29  type typeinfo struct {
    30  	decoder
    31  	writer
    32  }
    33  
    34  // represents struct tags
    35  type tags struct {
    36  	nilOK bool
    37  }
    38  
    39  type typekey struct {
    40  	reflect.Type
    41  	// the key must include the struct tags because they
    42  	// might generate a different decoder.
    43  	tags
    44  }
    45  
    46  type decoder func(*Stream, reflect.Value) error
    47  
    48  type writer func(reflect.Value, *encbuf) error
    49  
    50  func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) {
    51  	typeCacheMutex.RLock()
    52  	info := typeCache[typekey{typ, tags}]
    53  	typeCacheMutex.RUnlock()
    54  	if info != nil {
    55  		return info, nil
    56  	}
    57  	// not in the cache, need to generate info for this type.
    58  	typeCacheMutex.Lock()
    59  	defer typeCacheMutex.Unlock()
    60  	return cachedTypeInfo1(typ, tags)
    61  }
    62  
    63  func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) {
    64  	key := typekey{typ, tags}
    65  	info := typeCache[key]
    66  	if info != nil {
    67  		// another goroutine got the write lock first
    68  		return info, nil
    69  	}
    70  	// put a dummmy value into the cache before generating.
    71  	// if the generator tries to lookup itself, it will get
    72  	// the dummy value and won't call itself recursively.
    73  	typeCache[key] = new(typeinfo)
    74  	info, err := genTypeInfo(typ, tags)
    75  	if err != nil {
    76  		// remove the dummy value if the generator fails
    77  		delete(typeCache, key)
    78  		return nil, err
    79  	}
    80  	*typeCache[key] = *info
    81  	return typeCache[key], err
    82  }
    83  
    84  type field struct {
    85  	index int
    86  	info  *typeinfo
    87  }
    88  
    89  func structFields(typ reflect.Type) (fields []field, err error) {
    90  	for i := 0; i < typ.NumField(); i++ {
    91  		if f := typ.Field(i); f.PkgPath == "" { // exported
    92  			tags := parseStructTag(f.Tag.Get("rlp"))
    93  			info, err := cachedTypeInfo1(f.Type, tags)
    94  			if err != nil {
    95  				return nil, err
    96  			}
    97  			fields = append(fields, field{i, info})
    98  		}
    99  	}
   100  	return fields, nil
   101  }
   102  
   103  func parseStructTag(tag string) tags {
   104  	return tags{nilOK: tag == "nil"}
   105  }
   106  
   107  func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) {
   108  	info = new(typeinfo)
   109  	if info.decoder, err = makeDecoder(typ, tags); err != nil {
   110  		return nil, err
   111  	}
   112  	if info.writer, err = makeWriter(typ); err != nil {
   113  		return nil, err
   114  	}
   115  	return info, nil
   116  }
   117  
   118  func isUint(k reflect.Kind) bool {
   119  	return k >= reflect.Uint && k <= reflect.Uintptr
   120  }