Golang程序  |  185行  |  3.88 KB

// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package serializer

import (
	"reflect"

	"fmt"
	"io"
)

// Write writes Go-syntax representation of v into w.
// This is similar to fmt.Fprintf(w, "%#v", v), but properly handles pointers,
// does not write package names before types, omits struct fields with default values,
// omits type names where possible, etc. On the other hand, it currently does not
// support all types (e.g. channels and maps).
func Write(ww io.Writer, i interface{}) {
	w := writer{ww}
	v := reflect.ValueOf(i)
	if v.Kind() == reflect.Slice && (v.IsNil() || v.Len() == 0) {
		w.typ(v.Type())
		w.string("(nil)")
		return
	}
	w.do(v, false)
}

type writer struct {
	w io.Writer
}

func (w *writer) do(v reflect.Value, sliceElem bool) {
	switch v.Kind() {
	case reflect.Ptr:
		w.doPtr(v, sliceElem)
	case reflect.Interface:
		if v.IsNil() {
			w.string("nil")
		} else {
			w.do(v.Elem(), false)
		}
	case reflect.Slice:
		w.doSlice(v)
	case reflect.Struct:
		w.doStruct(v, sliceElem)
	case reflect.Bool:
		if v.Bool() {
			w.string("true")
		} else {
			w.string("false")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		fmt.Fprintf(w.w, "%v", v.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		fmt.Fprintf(w.w, "%v", v.Uint())
	case reflect.String:
		fmt.Fprintf(w.w, "%q", v.String())
	case reflect.Func:
		// Skip, no way to serialize this.
	default:
		panic(fmt.Sprintf("unsupported type: %#v", v.Type().String()))
	}
}

func (w *writer) doPtr(v reflect.Value, sliceElem bool) {
	if v.IsNil() {
		w.string("nil")
		return
	}
	if !sliceElem {
		w.byte('&')
	}
	if v.Elem().Kind() != reflect.Struct {
		panic(fmt.Sprintf("only pointers to structs are supported, got %v",
			v.Type().Name()))
	}
	w.do(v.Elem(), sliceElem)
}

func (w *writer) doSlice(v reflect.Value) {
	if v.IsNil() || v.Len() == 0 {
		w.string("nil")
		return
	}
	w.typ(v.Type())
	sub := v.Type().Elem().Kind()
	if sub == reflect.Ptr || sub == reflect.Interface || sub == reflect.Struct {
		// Elem per-line.
		w.string("{\n")
		for i := 0; i < v.Len(); i++ {
			w.do(v.Index(i), true)
			w.string(",\n")
		}
		w.byte('}')
		return
	}
	// All on one line.
	w.byte('{')
	for i := 0; i < v.Len(); i++ {
		if i > 0 {
			w.byte(',')
		}
		w.do(v.Index(i), true)
	}
	w.byte('}')
}

func (w *writer) doStruct(v reflect.Value, sliceElem bool) {
	if !sliceElem {
		w.string(v.Type().Name())
	}
	w.byte('{')
	needComma := false
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		if isDefaultValue(f) {
			continue
		}
		if needComma {
			w.byte(',')
		}
		w.string(v.Type().Field(i).Name)
		w.byte(':')
		w.do(f, false)
		needComma = true
	}
	w.byte('}')
}

func (w *writer) typ(t reflect.Type) {
	switch t.Kind() {
	case reflect.Ptr:
		w.byte('*')
		w.typ(t.Elem())
	case reflect.Slice:
		w.string("[]")
		w.typ(t.Elem())
	default:
		w.string(t.Name())
	}
}

func (w *writer) string(v string) {
	io.WriteString(w.w, v)
}

func (w *writer) byte(v byte) {
	if bw, ok := w.w.(io.ByteWriter); ok {
		bw.WriteByte(v)
	} else {
		w.w.Write([]byte{v})
	}
}

func isDefaultValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Ptr:
		return v.IsNil()
	case reflect.Interface:
		return v.IsNil()
	case reflect.Slice:
		return v.IsNil() || v.Len() == 0
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			if !isDefaultValue(v.Field(i)) {
				return false
			}
		}
		return true
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.String:
		return v.String() == ""
	case reflect.Func:
		return true
	default:
		return false
	}
}