pkg/util/reflect.go (181 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 util import ( "reflect" "runtime" "strings" "sync" "unsafe" "github.com/go-chassis/foundation/fileutil" ) var ( reflector *Reflector unknown = new(ReflectObject) ) func init() { reflector = &Reflector{ types: make(map[*uintptr]*ReflectObject), } } type ReflectObject struct { // full name FullName string Type reflect.Type // if type is not struct, Fields is nil Fields []reflect.StructField } // Name returns a short name of the object type func (o *ReflectObject) Name() string { return fileutil.LastNameOf(o.FullName) } type Reflector struct { types map[*uintptr]*ReflectObject mux sync.RWMutex } func (r *Reflector) Load(obj interface{}) *ReflectObject { r.mux.RLock() itab := *(**uintptr)(unsafe.Pointer(&obj)) t, ok := r.types[itab] r.mux.RUnlock() if ok { return t } r.mux.Lock() t, ok = r.types[itab] if ok { r.mux.Unlock() return t } t = new(ReflectObject) v := reflect.ValueOf(obj) if !v.IsValid() { r.mux.Unlock() return unknown } switch v.Kind() { case reflect.Ptr: fallthrough case reflect.Interface: r.mux.Unlock() if v.IsNil() { return unknown } e := v.Elem() if e.CanInterface() { return r.Load(e.Interface()) } return unknown default: t.Type = reflect.TypeOf(obj) switch v.Kind() { case reflect.Func: r.mux.Unlock() f := runtime.FuncForPC(v.Pointer()) if f == nil { return unknown } t.FullName = f.Name() return t case reflect.Struct: if fl := t.Type.NumField(); fl > 0 { t.Fields = make([]reflect.StructField, fl) for i := 0; i < fl; i++ { f := t.Type.Field(i) t.Fields[i] = f } } fallthrough default: t.FullName = t.Type.PkgPath() + "." + t.Type.Name() } } r.types[itab] = t r.mux.Unlock() return t } func Reflect(obj interface{}) *ReflectObject { return reflector.Load(obj) } func Sizeof(obj interface{}) uint64 { selfRecurseMap := make(map[uintptr]struct{}) return sizeof(reflect.ValueOf(obj), selfRecurseMap) } func sizeof(v reflect.Value, selfRecurseMap map[uintptr]struct{}) (s uint64) { if !v.IsValid() { return } if v.CanAddr() { selfRecurseMap[v.Addr().Pointer()] = struct{}{} } t := v.Type() s += uint64(t.Size()) switch v.Kind() { case reflect.Ptr: if v.IsNil() { break } if _, ok := selfRecurseMap[v.Pointer()]; ok { break } fallthrough case reflect.Interface: s += sizeof(v.Elem(), selfRecurseMap) case reflect.Struct: s -= uint64(t.Size()) for i := 0; i < v.NumField(); i++ { s += sizeof(v.Field(i), selfRecurseMap) } case reflect.Array: if isValueType(t.Elem().Kind()) { break } s -= uint64(t.Size()) for i := 0; i < v.Len(); i++ { s += sizeof(v.Index(i), selfRecurseMap) } case reflect.Slice: et := t.Elem() if isValueType(et.Kind()) { s += uint64(v.Len()) * uint64(et.Size()) break } for i := 0; i < v.Len(); i++ { s += sizeof(v.Index(i), selfRecurseMap) } case reflect.Map: if v.IsNil() { break } kt, vt := t.Key(), t.Elem() if isValueType(kt.Kind()) && isValueType(vt.Kind()) { s += uint64(kt.Size()+vt.Size()) * uint64(v.Len()) break } for _, k := range v.MapKeys() { s += sizeof(k, selfRecurseMap) s += sizeof(v.MapIndex(k), selfRecurseMap) } case reflect.String: s += uint64(v.Len()) } return } func isValueType(kind reflect.Kind) bool { switch kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: return true default: return false } } func FormatFuncName(f string) string { i := strings.LastIndex(f, "/") j := strings.Index(f[i+1:], ".") if j < 1 { return "???" } _, fun := f[:i+j+1], f[i+j+2:] i = strings.LastIndex(fun, ".") return strings.TrimSuffix(fun[i+1:], "-fm") // trim the suffix of function closure name } func FuncName(f interface{}) string { return Reflect(f).Name() }