github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/rlp/typecache.go (about)

     1  package rlp
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"sync"
     8  )
     9  
    10  var (
    11  	typeCacheMutex sync.RWMutex
    12  	typeCache      = make(map[typekey]*typeinfo)
    13  )
    14  
    15  type typeinfo struct {
    16  	decoder
    17  	writer
    18  }
    19  
    20  // represents struct tags
    21  type tags struct {
    22  	// rlp:"nil" controls whether empty input results in a nil pointer.
    23  	nilOK bool
    24  	// rlp:"tail" controls whether this field swallows additional list
    25  	// elements. It can only be set for the last field, which must be
    26  	// of slice type.
    27  	tail bool
    28  	// rlp:"-" ignores fields.
    29  	ignored bool
    30  }
    31  
    32  type typekey struct {
    33  	reflect.Type
    34  	// the key must include the struct tags because they
    35  	// might generate a different decoder.
    36  	tags
    37  }
    38  
    39  type decoder func(*Stream, reflect.Value) error
    40  
    41  type writer func(reflect.Value, *encbuf) error
    42  
    43  func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) {
    44  	typeCacheMutex.RLock()
    45  	info := typeCache[typekey{typ, tags}]
    46  	typeCacheMutex.RUnlock()
    47  	if info != nil {
    48  		return info, nil
    49  	}
    50  	// not in the cache, need to generate info for this type.
    51  	typeCacheMutex.Lock()
    52  	defer typeCacheMutex.Unlock()
    53  	return cachedTypeInfo1(typ, tags)
    54  }
    55  
    56  func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) {
    57  	key := typekey{typ, tags}
    58  	info := typeCache[key]
    59  	if info != nil {
    60  		// another goroutine got the write lock first
    61  		return info, nil
    62  	}
    63  	// put a dummmy value into the cache before generating.
    64  	// if the generator tries to lookup itself, it will get
    65  	// the dummy value and won't call itself recursively.
    66  	typeCache[key] = new(typeinfo)
    67  	info, err := genTypeInfo(typ, tags)
    68  	if err != nil {
    69  		// remove the dummy value if the generator fails
    70  		delete(typeCache, key)
    71  		return nil, err
    72  	}
    73  	*typeCache[key] = *info
    74  	return typeCache[key], err
    75  }
    76  
    77  type field struct {
    78  	index int
    79  	info  *typeinfo
    80  }
    81  
    82  func structFields(typ reflect.Type) (fields []field, err error) {
    83  	for i := 0; i < typ.NumField(); i++ {
    84  		if f := typ.Field(i); f.PkgPath == "" { // exported
    85  			tags, err := parseStructTag(typ, i)
    86  			if err != nil {
    87  				return nil, err
    88  			}
    89  			if tags.ignored {
    90  				continue
    91  			}
    92  			info, err := cachedTypeInfo1(f.Type, tags)
    93  			if err != nil {
    94  				return nil, err
    95  			}
    96  			fields = append(fields, field{i, info})
    97  		}
    98  	}
    99  	return fields, nil
   100  }
   101  
   102  func parseStructTag(typ reflect.Type, fi int) (tags, error) {
   103  	f := typ.Field(fi)
   104  	var ts tags
   105  	for _, t := range strings.Split(f.Tag.Get("rlp"), ",") {
   106  		switch t = strings.TrimSpace(t); t {
   107  		case "":
   108  		case "-":
   109  			ts.ignored = true
   110  		case "nil":
   111  			ts.nilOK = true
   112  		case "tail":
   113  			ts.tail = true
   114  			if fi != typ.NumField()-1 {
   115  				return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (must be on last field)`, typ, f.Name)
   116  			}
   117  			if f.Type.Kind() != reflect.Slice {
   118  				return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (field type is not slice)`, typ, f.Name)
   119  			}
   120  		default:
   121  			return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name)
   122  		}
   123  	}
   124  	return ts, nil
   125  }
   126  
   127  func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) {
   128  	info = new(typeinfo)
   129  	if info.decoder, err = makeDecoder(typ, tags); err != nil {
   130  		return nil, err
   131  	}
   132  	if info.writer, err = makeWriter(typ, tags); err != nil {
   133  		return nil, err
   134  	}
   135  	return info, nil
   136  }
   137  
   138  func isUint(k reflect.Kind) bool {
   139  	return k >= reflect.Uint && k <= reflect.Uintptr
   140  }