github.com/cloudwego/frugal@v0.1.15/internal/binary/defs/resolver.go (about)

     1  /*
     2   * Copyright 2022 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  
    17  package defs
    18  
    19  import (
    20      `fmt`
    21      `math/bits`
    22      `reflect`
    23      `sort`
    24      `strconv`
    25      `strings`
    26      `sync`
    27  )
    28  
    29  type (
    30      Options      uint8
    31  	Requiredness uint8
    32  )
    33  
    34  const (
    35      NoCopy Options = 1 << iota
    36  )
    37  
    38  const (
    39      Default Requiredness = iota
    40      Required
    41      Optional
    42  )
    43  
    44  func (self Options) String() string {
    45      nb := bits.OnesCount8(uint8(self))
    46      ret := make([]string, 0, nb)
    47  
    48      /* check for "nocopy" option */
    49      if self & NoCopy != 0 {
    50          ret = append(ret, "nocopy")
    51      }
    52  
    53      /* join them together */
    54      return fmt.Sprintf(
    55          "{%s}",
    56          strings.Join(ret, ","),
    57      )
    58  }
    59  
    60  func (self Requiredness) String() string {
    61      switch self {
    62          case Default  : return "default"
    63          case Required : return "required"
    64          case Optional : return "optional"
    65          default       : panic("unreachable")
    66      }
    67  }
    68  
    69  type Field struct {
    70      F       int
    71      ID      uint16
    72      Type    *Type
    73      Opts    Options
    74      Spec    Requiredness
    75      Default reflect.Value
    76  }
    77  
    78  var (
    79      fieldsLock  = new(sync.RWMutex)
    80      fieldsCache = make(map[reflect.Type][]Field)
    81  )
    82  
    83  func ResolveFields(vt reflect.Type) ([]Field, error) {
    84      var ok bool
    85      var ex error
    86      var fv []Field
    87  
    88      /* attempt to find in cache */
    89      fieldsLock.RLock()
    90      fv, ok = fieldsCache[vt]
    91      fieldsLock.RUnlock()
    92  
    93      /* check if it exists */
    94      if ok {
    95          return fv, nil
    96      }
    97  
    98      /* retry with write lock */
    99      fieldsLock.Lock()
   100      defer fieldsLock.Unlock()
   101  
   102      /* try again */
   103      if fv, ok = fieldsCache[vt]; ok {
   104          return fv, nil
   105      }
   106  
   107      /* still not found, do the actual resolving */
   108      if fv, ex = doResolveFields(vt); ex != nil {
   109          return nil, ex
   110      }
   111  
   112      /* update cache */
   113      fieldsCache[vt] = fv
   114      return fv, nil
   115  }
   116  
   117  func doResolveFields(vt reflect.Type) ([]Field, error) {
   118      var err error
   119      var ret []Field
   120      var mem reflect.Value
   121  
   122      /* field ID map and default values */
   123      val := reflect.New(vt)
   124      ids := make(map[uint64]struct{}, vt.NumField())
   125  
   126      /* check for default values */
   127      if def, ok := val.Interface().(DefaultInitializer); ok {
   128          mem = val.Elem()
   129          def.InitDefault()
   130      }
   131  
   132      /* traverse all the fields */
   133      for i := 0; i < vt.NumField(); i++ {
   134          var ok bool
   135          var pt *Type
   136          var id uint64
   137          var tv string
   138          var fv Options
   139          var ft []string
   140          var rx Requiredness
   141          var rv reflect.Value
   142          var sf reflect.StructField
   143  
   144          /* extract the field, ignore anonymous or private fields */
   145          if sf = vt.Field(i); sf.Anonymous || sf.PkgPath != "" {
   146              continue
   147          }
   148  
   149          /* ignore fields that does not declare the "frugal" tag */
   150          if tv, ok = sf.Tag.Lookup("frugal"); !ok {
   151              continue
   152          }
   153  
   154          /* must have at least 2 fields: ID and Requiredness */
   155          if ft = strings.Split(tv, ","); len(ft) < 2 {
   156              return nil, fmt.Errorf("invalid tag for field %s.%s", vt, sf.Name)
   157          }
   158  
   159          /* parse the field index */
   160          if id, err = strconv.ParseUint(strings.TrimSpace(ft[0]), 10, 16); err != nil {
   161              return nil, fmt.Errorf("invalid field number for field %s.%s: %w", vt, sf.Name, err)
   162          }
   163  
   164          /* convert the requiredness of this field */
   165          switch strings.TrimSpace(ft[1]) {
   166              case "default"  : rx = Default
   167              case "required" : rx = Required
   168              case "optional" : rx = Optional
   169              default         : return nil, fmt.Errorf("invalid requiredness for field %s.%s", vt, sf.Name)
   170          }
   171  
   172          /* check for duplicates */
   173          if _, ok = ids[id]; !ok {
   174              ids[id] = struct{}{}
   175          } else {
   176              return nil, fmt.Errorf("duplicated field ID %d for field %s.%s", id, vt, sf.Name)
   177          }
   178  
   179          /* types and other options are optional */
   180          if len(ft) == 2 {
   181              tv, ft = "", nil
   182          } else {
   183              tv, ft = strings.TrimSpace(ft[2]), ft[3:]
   184          }
   185  
   186          /* parse the type descriptor */
   187          if pt, err = ParseType(sf.Type, tv); err != nil {
   188              return nil, fmt.Errorf("cannot parse type descriptor: %w", err)
   189          }
   190  
   191          /* only optional fields or structs can be pointers */
   192          if rx != Optional && pt.T == T_pointer && pt.V.T != T_struct {
   193              return nil, fmt.Errorf("only optional fields or structs can be pointers, not %s: %s.%s", sf.Type, vt, sf.Name)
   194          }
   195  
   196          /* scan for the options */
   197          for _, opt := range ft {
   198              switch opt {
   199                  default: {
   200                      return nil, fmt.Errorf("invalid option: %s", opt)
   201                  }
   202  
   203                  /* "nocopy" option enables zero-copy string decoding */
   204                  case "nocopy": {
   205                      if pt.Tag() != T_string {
   206                          return nil, fmt.Errorf(`"nocopy" is only applicable to "string" and "binary" types, not %s`, pt)
   207                      } else if fv & NoCopy != 0 {
   208                          return nil, fmt.Errorf(`duplicated option "nocopy" for field %s.%s`, vt, sf.Name)
   209                      } else {
   210                          fv |= NoCopy
   211                      }
   212                  }
   213              }
   214          }
   215  
   216          /* get the default value if any */
   217          if mem.IsValid() {
   218              rv = mem.FieldByIndex(sf.Index)
   219          }
   220  
   221          /* add to result */
   222          ret = append(ret, Field {
   223              F       : int(sf.Offset),
   224              ID      : uint16(id),
   225              Type    : pt,
   226              Opts    : fv,
   227              Spec    : rx,
   228              Default : rv,
   229          })
   230      }
   231  
   232      /* sort the field by ID */
   233      sort.Slice(ret, func(i, j int) bool { return ret[i].ID < ret[j].ID })
   234      return ret, nil
   235  }