field.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package yu_proto_old
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. )
  9. type TagPrefix int
  10. // Possible tag options.
  11. const (
  12. TagNone TagPrefix = iota
  13. TagOptional
  14. TagRequired
  15. )
  16. func ParseTag(field reflect.StructField) (id int, opt TagPrefix, name string) {
  17. tag := field.Tag.Get("protobuf")
  18. if tag == "" {
  19. return
  20. }
  21. parts := strings.Split(tag, ",")
  22. for _, part := range parts {
  23. if part == "opt" {
  24. opt = TagOptional
  25. } else if part == "req" {
  26. opt = TagRequired
  27. } else {
  28. i, err := strconv.Atoi(part)
  29. if err != nil {
  30. name = part
  31. } else {
  32. id = int(i)
  33. }
  34. }
  35. }
  36. return
  37. }
  38. // ProtoField contains cached reflected metadata for struct fields.
  39. type ProtoField struct {
  40. ID int64
  41. Prefix TagPrefix
  42. Name string // If non-empty, tag-defined field name.
  43. Index []int
  44. Field reflect.StructField
  45. }
  46. func (p *ProtoField) Required() bool {
  47. return p.Prefix == TagRequired || p.Field.Type.Kind() != reflect.Ptr
  48. }
  49. var cache = map[reflect.Type][]*ProtoField{}
  50. var cacheLock sync.Mutex
  51. func ProtoFields(t reflect.Type) []*ProtoField {
  52. cacheLock.Lock()
  53. idx, ok := cache[t]
  54. cacheLock.Unlock()
  55. if ok {
  56. return idx
  57. }
  58. id := 0
  59. idx = innerFieldIndexes(&id, t)
  60. seen := map[int64]struct{}{}
  61. for _, i := range idx {
  62. if _, ok := seen[i.ID]; ok {
  63. panic(fmt.Sprintf("protobuf ID %d reused in %s.%s", i.ID, t.PkgPath(), t.Name()))
  64. }
  65. seen[i.ID] = struct{}{}
  66. }
  67. cacheLock.Lock()
  68. defer cacheLock.Unlock()
  69. cache[t] = idx
  70. return idx
  71. }
  72. func innerFieldIndexes(id *int, v reflect.Type) []*ProtoField {
  73. if v.Kind() == reflect.Ptr {
  74. return innerFieldIndexes(id, v.Elem())
  75. }
  76. out := []*ProtoField{}
  77. for i := 0; i < v.NumField(); i++ {
  78. f := v.Field(i)
  79. *id++
  80. tid, prefix, name := ParseTag(f)
  81. if tid != 0 {
  82. *id = tid
  83. }
  84. if f.Anonymous {
  85. *id--
  86. for _, inner := range innerFieldIndexes(id, f.Type) {
  87. inner.Index = append([]int{i}, inner.Index...)
  88. out = append(out, inner)
  89. }
  90. } else {
  91. out = append(out, &ProtoField{
  92. ID: int64(*id),
  93. Prefix: prefix,
  94. Name: name,
  95. Index: []int{i},
  96. Field: f,
  97. })
  98. }
  99. }
  100. return out
  101. }