resolver.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright 2021 ByteDance Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package resolver
  17. import (
  18. "fmt"
  19. "reflect"
  20. "strings"
  21. "sync"
  22. )
  23. type FieldOpts int
  24. type OffsetType int
  25. const (
  26. F_omitempty FieldOpts = 1 << iota
  27. F_stringize
  28. F_omitzero
  29. )
  30. const (
  31. F_offset OffsetType = iota
  32. F_deref
  33. )
  34. type Offset struct {
  35. Size uintptr
  36. Kind OffsetType
  37. Type reflect.Type
  38. }
  39. type FieldMeta struct {
  40. Name string
  41. Path []Offset
  42. Opts FieldOpts
  43. Type reflect.Type
  44. IsZero func(reflect.Value) bool
  45. }
  46. func (self *FieldMeta) String() string {
  47. var path []string
  48. var opts []string
  49. /* dump the field path */
  50. for _, off := range self.Path {
  51. if off.Kind == F_offset {
  52. path = append(path, fmt.Sprintf("%d", off.Size))
  53. } else {
  54. path = append(path, fmt.Sprintf("%d.(*%s)", off.Size, off.Type))
  55. }
  56. }
  57. /* check for "string" */
  58. if (self.Opts & F_stringize) != 0 {
  59. opts = append(opts, "string")
  60. }
  61. /* check for "omitempty" */
  62. if (self.Opts & F_omitempty) != 0 {
  63. opts = append(opts, "omitempty")
  64. }
  65. /* format the field */
  66. return fmt.Sprintf(
  67. "{Field \"%s\" @ %s, opts=%s, type=%s}",
  68. self.Name,
  69. strings.Join(path, "."),
  70. strings.Join(opts, ","),
  71. self.Type,
  72. )
  73. }
  74. func (self *FieldMeta) optimize() {
  75. var n int
  76. var v uintptr
  77. /* merge adjacent offsets */
  78. for _, o := range self.Path {
  79. if v += o.Size; o.Kind == F_deref {
  80. self.Path[n].Size = v
  81. self.Path[n].Type, v = o.Type, 0
  82. self.Path[n].Kind, n = F_deref, n + 1
  83. }
  84. }
  85. /* last offset value */
  86. if v != 0 {
  87. self.Path[n].Size = v
  88. self.Path[n].Type = nil
  89. self.Path[n].Kind = F_offset
  90. n++
  91. }
  92. /* must be at least 1 offset */
  93. if n != 0 {
  94. self.Path = self.Path[:n]
  95. } else {
  96. self.Path = []Offset{{Kind: F_offset}}
  97. }
  98. }
  99. func resolveFields(vt reflect.Type) []FieldMeta {
  100. tfv := typeFields(vt)
  101. ret := []FieldMeta(nil)
  102. /* convert each field */
  103. for _, fv := range tfv.list {
  104. /* add to result */
  105. ret = append(ret, FieldMeta{})
  106. fm := &ret[len(ret)-1]
  107. item := vt
  108. path := []Offset(nil)
  109. /* check for "string" */
  110. if fv.quoted {
  111. fm.Opts |= F_stringize
  112. }
  113. /* check for "omitempty" */
  114. if fv.omitEmpty {
  115. fm.Opts |= F_omitempty
  116. }
  117. /* handle the "omitzero" */
  118. handleOmitZero(fv, fm)
  119. /* dump the field path */
  120. for _, i := range fv.index {
  121. kind := F_offset
  122. fval := item.Field(i)
  123. item = fval.Type
  124. /* deref the pointer if needed */
  125. if item.Kind() == reflect.Ptr {
  126. kind = F_deref
  127. item = item.Elem()
  128. }
  129. /* add to path */
  130. path = append(path, Offset {
  131. Kind: kind,
  132. Type: item,
  133. Size: fval.Offset,
  134. })
  135. }
  136. /* get the index to the last offset */
  137. idx := len(path) - 1
  138. fvt := path[idx].Type
  139. /* do not dereference into fields */
  140. if path[idx].Kind == F_deref {
  141. fvt = reflect.PtrTo(fvt)
  142. path[idx].Kind = F_offset
  143. }
  144. fm.Type = fvt
  145. fm.Path = path
  146. fm.Name = fv.name
  147. }
  148. /* optimize the offsets */
  149. for i := range ret {
  150. ret[i].optimize()
  151. }
  152. /* all done */
  153. return ret
  154. }
  155. var (
  156. fieldLock = sync.RWMutex{}
  157. fieldCache = map[reflect.Type][]FieldMeta{}
  158. )
  159. func ResolveStruct(vt reflect.Type) []FieldMeta {
  160. var ok bool
  161. var fm []FieldMeta
  162. /* attempt to read from cache */
  163. fieldLock.RLock()
  164. fm, ok = fieldCache[vt]
  165. fieldLock.RUnlock()
  166. /* check if it was cached */
  167. if ok {
  168. return fm
  169. }
  170. /* otherwise use write-lock */
  171. fieldLock.Lock()
  172. defer fieldLock.Unlock()
  173. /* double check */
  174. if fm, ok = fieldCache[vt]; ok {
  175. return fm
  176. }
  177. /* resolve the field */
  178. fm = resolveFields(vt)
  179. fieldCache[vt] = fm
  180. return fm
  181. }