另客网go项目公用的代码库

cache.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package validator
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. "sync"
  7. "sync/atomic"
  8. )
  9. type tagType uint8
  10. const (
  11. typeDefault tagType = iota
  12. typeOmitEmpty
  13. typeNoStructLevel
  14. typeStructOnly
  15. typeDive
  16. typeOr
  17. typeExists
  18. )
  19. type structCache struct {
  20. lock sync.Mutex
  21. m atomic.Value // map[reflect.Type]*cStruct
  22. }
  23. func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) {
  24. c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key]
  25. return
  26. }
  27. func (sc *structCache) Set(key reflect.Type, value *cStruct) {
  28. m := sc.m.Load().(map[reflect.Type]*cStruct)
  29. nm := make(map[reflect.Type]*cStruct, len(m)+1)
  30. for k, v := range m {
  31. nm[k] = v
  32. }
  33. nm[key] = value
  34. sc.m.Store(nm)
  35. }
  36. type tagCache struct {
  37. lock sync.Mutex
  38. m atomic.Value // map[string]*cTag
  39. }
  40. func (tc *tagCache) Get(key string) (c *cTag, found bool) {
  41. c, found = tc.m.Load().(map[string]*cTag)[key]
  42. return
  43. }
  44. func (tc *tagCache) Set(key string, value *cTag) {
  45. m := tc.m.Load().(map[string]*cTag)
  46. nm := make(map[string]*cTag, len(m)+1)
  47. for k, v := range m {
  48. nm[k] = v
  49. }
  50. nm[key] = value
  51. tc.m.Store(nm)
  52. }
  53. type cStruct struct {
  54. Name string
  55. fields map[int]*cField
  56. fn StructLevelFunc
  57. }
  58. type cField struct {
  59. Idx int
  60. Name string
  61. AltName string
  62. cTags *cTag
  63. }
  64. type cTag struct {
  65. tag string
  66. aliasTag string
  67. actualAliasTag string
  68. param string
  69. hasAlias bool
  70. typeof tagType
  71. hasTag bool
  72. fn Func
  73. next *cTag
  74. }
  75. func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
  76. v.structCache.lock.Lock()
  77. defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise!
  78. typ := current.Type()
  79. // could have been multiple trying to access, but once first is done this ensures struct
  80. // isn't parsed again.
  81. cs, ok := v.structCache.Get(typ)
  82. if ok {
  83. return cs
  84. }
  85. cs = &cStruct{Name: sName, fields: make(map[int]*cField), fn: v.structLevelFuncs[typ]}
  86. numFields := current.NumField()
  87. var ctag *cTag
  88. var fld reflect.StructField
  89. var tag string
  90. var customName string
  91. for i := 0; i < numFields; i++ {
  92. fld = typ.Field(i)
  93. if !fld.Anonymous && fld.PkgPath != blank {
  94. continue
  95. }
  96. tag = fld.Tag.Get(v.tagName)
  97. if tag == skipValidationTag {
  98. continue
  99. }
  100. customName = fld.Name
  101. if v.fieldNameTag != blank {
  102. name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
  103. // dash check is for json "-" (aka skipValidationTag) means don't output in json
  104. if name != "" && name != skipValidationTag {
  105. customName = name
  106. }
  107. }
  108. // NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different
  109. // and so only struct level caching can be used instead of combined with Field tag caching
  110. if len(tag) > 0 {
  111. ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, blank, false)
  112. } else {
  113. // even if field doesn't have validations need cTag for traversing to potential inner/nested
  114. // elements of the field.
  115. ctag = new(cTag)
  116. }
  117. cs.fields[i] = &cField{Idx: i, Name: fld.Name, AltName: customName, cTags: ctag}
  118. }
  119. v.structCache.Set(typ, cs)
  120. return cs
  121. }
  122. func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
  123. var t string
  124. var ok bool
  125. noAlias := len(alias) == 0
  126. tags := strings.Split(tag, tagSeparator)
  127. for i := 0; i < len(tags); i++ {
  128. t = tags[i]
  129. if noAlias {
  130. alias = t
  131. }
  132. if v.hasAliasValidators {
  133. // check map for alias and process new tags, otherwise process as usual
  134. if tagsVal, found := v.aliasValidators[t]; found {
  135. if i == 0 {
  136. firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
  137. } else {
  138. next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
  139. current.next, current = next, curr
  140. }
  141. continue
  142. }
  143. }
  144. if i == 0 {
  145. current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true}
  146. firstCtag = current
  147. } else {
  148. current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true}
  149. current = current.next
  150. }
  151. switch t {
  152. case diveTag:
  153. current.typeof = typeDive
  154. continue
  155. case omitempty:
  156. current.typeof = typeOmitEmpty
  157. continue
  158. case structOnlyTag:
  159. current.typeof = typeStructOnly
  160. continue
  161. case noStructLevelTag:
  162. current.typeof = typeNoStructLevel
  163. continue
  164. case existsTag:
  165. current.typeof = typeExists
  166. continue
  167. default:
  168. // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
  169. orVals := strings.Split(t, orSeparator)
  170. for j := 0; j < len(orVals); j++ {
  171. vals := strings.SplitN(orVals[j], tagKeySeparator, 2)
  172. if noAlias {
  173. alias = vals[0]
  174. current.aliasTag = alias
  175. } else {
  176. current.actualAliasTag = t
  177. }
  178. if j > 0 {
  179. current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true}
  180. current = current.next
  181. }
  182. current.tag = vals[0]
  183. if len(current.tag) == 0 {
  184. panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
  185. }
  186. if current.fn, ok = v.validationFuncs[current.tag]; !ok {
  187. panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, fieldName)))
  188. }
  189. if len(orVals) > 1 {
  190. current.typeof = typeOr
  191. }
  192. if len(vals) > 1 {
  193. current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
  194. }
  195. }
  196. }
  197. }
  198. return
  199. }