go 使用 reflect 实现 vardump

利用 cache 在一些方面做了优化, 总体性能还是不错

这个实现会被合并到 https://github.com/vizee/echo 作为 echo.Var 的输出实现

package vardump

import (
    "io"
    "reflect"
    "sync"
    "unsafe"

    "github.com/vizee/litebuf"
)

var rtcache struct {
    sync.RWMutex
    types map[reflect.Type]*rtype
}

var bufpool = sync.Pool{
    New: func() interface{} {
        return &litebuf.Buffer{}
    },
}

type rtype struct {
    typ    string
    fields []string
}

func makertype(rt reflect.Type) *rtype {
    n := rt.NumField()
    t := &rtype{
        typ:    rt.String(),
        fields: make([]string, n),
    }
    for i := 0; i < n; i++ {
        rf := rt.Field(i)
        t.fields[i] = rf.Name
        if rf.Type.Kind() == reflect.Struct {
            if _, ok := rtcache.types[rf.Type]; !ok {
                makertype(rf.Type)
            }
        }
    }
    rtcache.types[rt] = t
    return t
}

func getrtype(rt reflect.Type) *rtype {
    rtcache.RLock()
    t := rtcache.types[rt]
    rtcache.RUnlock()

    if t == nil {
        rtcache.Lock()
        t = rtcache.types[rt]
        if t == nil {
            t = makertype(rt)
        }
        rtcache.Unlock()
    }
    return t
}

type vari struct {
    maxDeep int
    deep    int
    buf     *litebuf.Buffer
}

func (v *vari) dumpStruct(rv reflect.Value) {
    rt := getrtype(rv.Type())
    v.buf.WriteString(rt.typ)
    v.buf.WriteByte('{')
    for i := 0; i < len(rt.fields); i++ {
        if i > 0 {
            v.buf.WriteByte(' ')
        }
        v.buf.WriteString(rt.fields[i])
        v.buf.WriteByte(':')
        t := rv.Field(i)
        if t.Kind() == reflect.Interface && t.CanInterface() {
            v.dump(t.Interface())
        } else {
            v.dumpValue(t)
        }
    }
    v.buf.WriteByte('}')
}

func (v *vari) dumpMap(rv reflect.Value) {
    v.buf.WriteString(`map[`)
    keys := rv.MapKeys()
    for i := range keys {
        if i > 0 {
            v.buf.WriteByte(' ')
        }
        v.dumpValue(keys[i])
        v.buf.WriteByte(':')
        t := rv.MapIndex(keys[i])
        if t.Kind() == reflect.Interface {
            v.dump(t.Interface())
        } else {
            v.dumpValue(t)
        }
    }
    v.buf.WriteByte(']')
}

func (v *vari) dumpArray(rv reflect.Value) {
    v.buf.WriteByte('[')
    n := rv.Len()
    for i := 0; i < n; i++ {
        if i > 0 {
            v.buf.WriteByte(' ')
        }
        t := rv.Index(i)
        if t.Kind() == reflect.Interface && t.CanInterface() {
            v.dump(t.Interface())
        } else {
            v.dumpValue(t)
        }
    }
    v.buf.WriteByte(']')
}

func (v *vari) dumpValue(rv reflect.Value) {
    switch rv.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        v.buf.AppendInt(rv.Int(), 10)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
        v.buf.AppendUint(rv.Uint(), 10)
    case reflect.Ptr:
        if v.deep < v.maxDeep {
            if rv.IsNil() {
                v.buf.WriteString("nil")
            } else {
                v.buf.WriteByte('&')
                v.deep++
                v.dumpValue(rv.Elem())
                v.deep--
            }
            break
        }
        fallthrough
    case reflect.UnsafePointer, reflect.Chan, reflect.Func:
        p := rv.Pointer()
        if p == 0 {
            v.buf.WriteString("nil")
        } else {
            v.buf.WriteString("0x")
            v.buf.AppendUint(uint64(p), 16)
        }
    case reflect.Bool:
        if rv.Bool() {
            v.buf.WriteString("true")
        } else {
            v.buf.WriteString("false")
        }
    case reflect.Float32:
        v.buf.AppendFloat(rv.Float(), 'g', -1, 32)
    case reflect.Float64:
        v.buf.AppendFloat(rv.Float(), 'g', -1, 64)
    case reflect.String:
        v.buf.WriteString(rv.String())
    case reflect.Interface:
        if rv.IsNil() {
            v.buf.WriteString("nil")
        } else if rv.CanInterface() && v.deep < v.maxDeep {
            v.deep++
            v.dumpValue(reflect.ValueOf(rv.Interface()))
            v.deep--
        } else {
            p := rv.InterfaceData()
            v.buf.WriteString("{0x")
            v.buf.AppendUint(uint64(p[0]), 16)
            v.buf.WriteString(",0x")
            v.buf.AppendUint(uint64(p[1]), 16)
            v.buf.WriteByte('}')
        }
    case reflect.Complex64, reflect.Complex128:
        c := rv.Complex()
        v.buf.AppendFloat(real(c), 'g', -1, 64)
        v.buf.WriteByte('+')
        v.buf.AppendFloat(imag(c), 'g', -1, 64)
        v.buf.WriteByte('i')
    case reflect.Map:
        if rv.IsNil() {
            v.buf.WriteString("nil")
        } else {
            v.dumpMap(rv)
        }
    case reflect.Struct:
        v.dumpStruct(rv)
    case reflect.Array, reflect.Slice:
        v.dumpArray(rv)
    default:
        v.buf.WriteString("<invalid kind>")
    }
}

func (v *vari) dump(x interface{}) {
    switch t := x.(type) {
    case nil:
        v.buf.WriteString("nil")
    case int:
        v.buf.AppendInt(int64(t), 10)
    case int8:
        v.buf.AppendInt(int64(t), 10)
    case int16:
        v.buf.AppendInt(int64(t), 10)
    case int32:
        v.buf.AppendInt(int64(t), 10)
    case int64:
        v.buf.AppendInt(t, 10)
    case uint:
        v.buf.AppendUint(uint64(t), 10)
    case uint8:
        v.buf.AppendUint(uint64(t), 10)
    case uint16:
        v.buf.AppendUint(uint64(t), 10)
    case uint32:
        v.buf.AppendUint(uint64(t), 10)
    case uint64:
        v.buf.AppendUint(t, 10)
    case uintptr:
        v.buf.AppendUint(uint64(t), 10)
    case float32:
        v.buf.AppendFloat(float64(t), 'g', -1, 32)
    case float64:
        v.buf.AppendFloat(t, 'g', -1, 64)
    case bool:
        if t {
            v.buf.WriteString("true")
        } else {
            v.buf.WriteString("false")
        }
    case unsafe.Pointer:
        v.buf.WriteString("0x")
        v.buf.AppendUint(uint64(uintptr(t)), 16)
    case string:
        v.buf.WriteString(t)
    case reflect.Value:
        v.dumpValue(t)
    default:
        v.dumpValue(reflect.ValueOf(t))
    }
}

func Dump(w io.Writer, x interface{}) {
    buf := bufpool.Get().(*litebuf.Buffer)
    v := vari{
        maxDeep: 1,
        buf:     buf,
    }
    v.dump(x)
    w.Write(buf.Bytes())
    bufpool.Put(buf)
}

func init() {
    rtcache.types = make(map[reflect.Type]*rtype)
}

标签: go, reflect

添加新评论