123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- /*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package resolver
- import (
- `fmt`
- `reflect`
- `strings`
- `sync`
- )
- type FieldOpts int
- type OffsetType int
- const (
- F_omitempty FieldOpts = 1 << iota
- F_stringize
- )
- const (
- F_offset OffsetType = iota
- F_deref
- )
- type Offset struct {
- Size uintptr
- Kind OffsetType
- Type reflect.Type
- }
- type FieldMeta struct {
- Name string
- Path []Offset
- Opts FieldOpts
- Type reflect.Type
- }
- func (self *FieldMeta) String() string {
- var path []string
- var opts []string
- /* dump the field path */
- for _, off := range self.Path {
- if off.Kind == F_offset {
- path = append(path, fmt.Sprintf("%d", off.Size))
- } else {
- path = append(path, fmt.Sprintf("%d.(*%s)", off.Size, off.Type))
- }
- }
- /* check for "string" */
- if (self.Opts & F_stringize) != 0 {
- opts = append(opts, "string")
- }
- /* check for "omitempty" */
- if (self.Opts & F_omitempty) != 0 {
- opts = append(opts, "omitempty")
- }
- /* format the field */
- return fmt.Sprintf(
- "{Field \"%s\" @ %s, opts=%s, type=%s}",
- self.Name,
- strings.Join(path, "."),
- strings.Join(opts, ","),
- self.Type,
- )
- }
- func (self *FieldMeta) optimize() {
- var n int
- var v uintptr
- /* merge adjacent offsets */
- for _, o := range self.Path {
- if v += o.Size; o.Kind == F_deref {
- self.Path[n].Size = v
- self.Path[n].Type, v = o.Type, 0
- self.Path[n].Kind, n = F_deref, n + 1
- }
- }
- /* last offset value */
- if v != 0 {
- self.Path[n].Size = v
- self.Path[n].Type = nil
- self.Path[n].Kind = F_offset
- n++
- }
- /* must be at least 1 offset */
- if n != 0 {
- self.Path = self.Path[:n]
- } else {
- self.Path = []Offset{{Kind: F_offset}}
- }
- }
- func resolveFields(vt reflect.Type) []FieldMeta {
- tfv := typeFields(vt)
- ret := []FieldMeta(nil)
- /* convert each field */
- for _, fv := range tfv.list {
- item := vt
- path := []Offset(nil)
- opts := FieldOpts(0)
- /* check for "string" */
- if fv.quoted {
- opts |= F_stringize
- }
- /* check for "omitempty" */
- if fv.omitEmpty {
- opts |= F_omitempty
- }
- /* dump the field path */
- for _, i := range fv.index {
- kind := F_offset
- fval := item.Field(i)
- item = fval.Type
- /* deref the pointer if needed */
- if item.Kind() == reflect.Ptr {
- kind = F_deref
- item = item.Elem()
- }
- /* add to path */
- path = append(path, Offset {
- Kind: kind,
- Type: item,
- Size: fval.Offset,
- })
- }
- /* get the index to the last offset */
- idx := len(path) - 1
- fvt := path[idx].Type
- /* do not dereference into fields */
- if path[idx].Kind == F_deref {
- fvt = reflect.PtrTo(fvt)
- path[idx].Kind = F_offset
- }
- /* add to result */
- ret = append(ret, FieldMeta {
- Type: fvt,
- Opts: opts,
- Path: path,
- Name: fv.name,
- })
- }
- /* optimize the offsets */
- for i := range ret {
- ret[i].optimize()
- }
- /* all done */
- return ret
- }
- var (
- fieldLock = sync.RWMutex{}
- fieldCache = map[reflect.Type][]FieldMeta{}
- )
- func ResolveStruct(vt reflect.Type) []FieldMeta {
- var ok bool
- var fm []FieldMeta
- /* attempt to read from cache */
- fieldLock.RLock()
- fm, ok = fieldCache[vt]
- fieldLock.RUnlock()
- /* check if it was cached */
- if ok {
- return fm
- }
- /* otherwise use write-lock */
- fieldLock.Lock()
- defer fieldLock.Unlock()
- /* double check */
- if fm, ok = fieldCache[vt]; ok {
- return fm
- }
- /* resolve the field */
- fm = resolveFields(vt)
- fieldCache[vt] = fm
- return fm
- }
|