summaryrefslogtreecommitdiff
path: root/vendor/github.com/bytedance/sonic/internal/resolver/resolver.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/bytedance/sonic/internal/resolver/resolver.go')
-rw-r--r--vendor/github.com/bytedance/sonic/internal/resolver/resolver.go214
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
+}