gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

resolver.go (4660B)


      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 
     17 package resolver
     18 
     19 import (
     20     `fmt`
     21     `reflect`
     22     `strings`
     23     `sync`
     24 )
     25 
     26 type FieldOpts int
     27 type OffsetType int
     28 
     29 const (
     30     F_omitempty FieldOpts = 1 << iota
     31     F_stringize
     32 )
     33 
     34 const (
     35     F_offset OffsetType = iota
     36     F_deref
     37 )
     38 
     39 type Offset struct {
     40     Size uintptr
     41     Kind OffsetType
     42     Type reflect.Type
     43 }
     44 
     45 type FieldMeta struct {
     46     Name string
     47     Path []Offset
     48     Opts FieldOpts
     49     Type reflect.Type
     50 }
     51 
     52 func (self *FieldMeta) String() string {
     53     var path []string
     54     var opts []string
     55 
     56     /* dump the field path */
     57     for _, off := range self.Path {
     58         if off.Kind == F_offset {
     59             path = append(path, fmt.Sprintf("%d", off.Size))
     60         } else {
     61             path = append(path, fmt.Sprintf("%d.(*%s)", off.Size, off.Type))
     62         }
     63     }
     64 
     65     /* check for "string" */
     66     if (self.Opts & F_stringize) != 0 {
     67         opts = append(opts, "string")
     68     }
     69 
     70     /* check for "omitempty" */
     71     if (self.Opts & F_omitempty) != 0 {
     72         opts = append(opts, "omitempty")
     73     }
     74 
     75     /* format the field */
     76     return fmt.Sprintf(
     77         "{Field \"%s\" @ %s, opts=%s, type=%s}",
     78         self.Name,
     79         strings.Join(path, "."),
     80         strings.Join(opts, ","),
     81         self.Type,
     82     )
     83 }
     84 
     85 func (self *FieldMeta) optimize() {
     86     var n int
     87     var v uintptr
     88 
     89     /* merge adjacent offsets */
     90     for _, o := range self.Path {
     91         if v += o.Size; o.Kind == F_deref {
     92             self.Path[n].Size    = v
     93             self.Path[n].Type, v = o.Type, 0
     94             self.Path[n].Kind, n = F_deref, n + 1
     95         }
     96     }
     97 
     98     /* last offset value */
     99     if v != 0 {
    100         self.Path[n].Size = v
    101         self.Path[n].Type = nil
    102         self.Path[n].Kind = F_offset
    103         n++
    104     }
    105 
    106     /* must be at least 1 offset */
    107     if n != 0 {
    108         self.Path = self.Path[:n]
    109     } else {
    110         self.Path = []Offset{{Kind: F_offset}}
    111     }
    112 }
    113 
    114 func resolveFields(vt reflect.Type) []FieldMeta {
    115     tfv := typeFields(vt)
    116     ret := []FieldMeta(nil)
    117 
    118     /* convert each field */
    119     for _, fv := range tfv.list {
    120         item := vt
    121         path := []Offset(nil)
    122         opts := FieldOpts(0)
    123 
    124         /* check for "string" */
    125         if fv.quoted {
    126             opts |= F_stringize
    127         }
    128 
    129         /* check for "omitempty" */
    130         if fv.omitEmpty {
    131             opts |= F_omitempty
    132         }
    133 
    134         /* dump the field path */
    135         for _, i := range fv.index {
    136             kind := F_offset
    137             fval := item.Field(i)
    138             item  = fval.Type
    139 
    140             /* deref the pointer if needed */
    141             if item.Kind() == reflect.Ptr {
    142                 kind = F_deref
    143                 item = item.Elem()
    144             }
    145 
    146             /* add to path */
    147             path = append(path, Offset {
    148                 Kind: kind,
    149                 Type: item,
    150                 Size: fval.Offset,
    151             })
    152         }
    153 
    154         /* get the index to the last offset */
    155         idx := len(path) - 1
    156         fvt := path[idx].Type
    157 
    158         /* do not dereference into fields */
    159         if path[idx].Kind == F_deref {
    160             fvt = reflect.PtrTo(fvt)
    161             path[idx].Kind = F_offset
    162         }
    163 
    164         /* add to result */
    165         ret = append(ret, FieldMeta {
    166             Type: fvt,
    167             Opts: opts,
    168             Path: path,
    169             Name: fv.name,
    170         })
    171     }
    172 
    173     /* optimize the offsets */
    174     for i := range ret {
    175         ret[i].optimize()
    176     }
    177 
    178     /* all done */
    179     return ret
    180 }
    181 
    182 var (
    183     fieldLock  = sync.RWMutex{}
    184     fieldCache = map[reflect.Type][]FieldMeta{}
    185 )
    186 
    187 func ResolveStruct(vt reflect.Type) []FieldMeta {
    188     var ok bool
    189     var fm []FieldMeta
    190 
    191     /* attempt to read from cache */
    192     fieldLock.RLock()
    193     fm, ok = fieldCache[vt]
    194     fieldLock.RUnlock()
    195 
    196     /* check if it was cached */
    197     if ok {
    198         return fm
    199     }
    200 
    201     /* otherwise use write-lock */
    202     fieldLock.Lock()
    203     defer fieldLock.Unlock()
    204 
    205     /* double check */
    206     if fm, ok = fieldCache[vt]; ok {
    207         return fm
    208     }
    209 
    210     /* resolve the field */
    211     fm = resolveFields(vt)
    212     fieldCache[vt] = fm
    213     return fm
    214 }