diff options
Diffstat (limited to 'vendor/github.com/bytedance/sonic/internal/resolver/resolver.go')
-rw-r--r-- | vendor/github.com/bytedance/sonic/internal/resolver/resolver.go | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/vendor/github.com/bytedance/sonic/internal/resolver/resolver.go b/vendor/github.com/bytedance/sonic/internal/resolver/resolver.go new file mode 100644 index 000000000..796d5d823 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/internal/resolver/resolver.go @@ -0,0 +1,214 @@ +/* + * 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 */ + fvt := fv.typ + idx := len(path) - 1 + + /* 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 +} |