typecache.go 4.0 KB

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